riched20: Add tests for EM_GETMODIFY.
[wine] / dlls / riched20 / tests / editor.c
1 /*
2 * Unit test suite for rich edit control
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <wine/test.h>
22 #include <windows.h>
23 #include <richedit.h>
24 #include <time.h>
25
26 static HMODULE hmoduleRichEdit;
27
28 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
29   HWND hwnd;
30   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
31                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
32                       hmoduleRichEdit, NULL);
33   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
34   return hwnd;
35 }
36
37 static HWND new_richedit(HWND parent) {
38   return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
39 }
40
41 static const char haystack[] = "WINEWine wineWine wine WineWine";
42                              /* ^0        ^10       ^20       ^30 */
43
44 struct find_s {
45   int start;
46   int end;
47   const char *needle;
48   int flags;
49   int expected_loc;
50   int _todo_wine;
51 };
52
53
54 struct find_s find_tests[] = {
55   /* Find in empty text */
56   {0, -1, "foo", FR_DOWN, -1, 0},
57   {0, -1, "foo", 0, -1, 0},
58   {0, -1, "", FR_DOWN, -1, 0},
59   {20, 5, "foo", FR_DOWN, -1, 0},
60   {5, 20, "foo", FR_DOWN, -1, 0}
61 };
62
63 struct find_s find_tests2[] = {
64   /* No-result find */
65   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
66   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
67
68   /* Subsequent finds */
69   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
70   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
71   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
72   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
73
74   /* Find backwards */
75   {19, 20, "Wine", FR_MATCHCASE, 13, 0},
76   {10, 20, "Wine", FR_MATCHCASE, 4, 0},
77   {20, 10, "Wine", FR_MATCHCASE, 13, 0},
78
79   /* Case-insensitive */
80   {1, 31, "wInE", FR_DOWN, 4, 0},
81   {1, 31, "Wine", FR_DOWN, 4, 0},
82
83   /* High-to-low ranges */
84   {20, 5, "Wine", FR_DOWN, -1, 0},
85   {2, 1, "Wine", FR_DOWN, -1, 0},
86   {30, 29, "Wine", FR_DOWN, -1, 0},
87   {20, 5, "Wine", 0, 13, 0},
88
89   /* Find nothing */
90   {5, 10, "", FR_DOWN, -1, 0},
91   {10, 5, "", FR_DOWN, -1, 0},
92   {0, -1, "", FR_DOWN, -1, 0},
93   {10, 5, "", 0, -1, 0},
94
95   /* Whole-word search */
96   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
97   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
98   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
99   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
100   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
101   {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
102   {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
103   
104   /* Bad ranges */
105   {5, 200, "XXX", FR_DOWN, -1, 0},
106   {-20, 20, "Wine", FR_DOWN, -1, 0},
107   {-20, 20, "Wine", FR_DOWN, -1, 0},
108   {-15, -20, "Wine", FR_DOWN, -1, 0},
109   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
110
111   /* Check the case noted in bug 4479 where matches at end aren't recognized */
112   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
113   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
114   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
115   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
116   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
117
118   /* The backwards case of bug 4479; bounds look right
119    * Fails because backward find is wrong */
120   {19, 20, "WINE", FR_MATCHCASE, 0, 0},
121   {0, 20, "WINE", FR_MATCHCASE, -1, 0}
122 };
123
124 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
125   int findloc;
126   FINDTEXT ft;
127   memset(&ft, 0, sizeof(ft));
128   ft.chrg.cpMin = f->start;
129   ft.chrg.cpMax = f->end;
130   ft.lpstrText = f->needle;
131   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
132   ok(findloc == f->expected_loc,
133      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
134      name, id, f->needle, f->start, f->end, f->flags, findloc);
135 }
136
137 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
138     int id) {
139   int findloc;
140   FINDTEXTEX ft;
141   memset(&ft, 0, sizeof(ft));
142   ft.chrg.cpMin = f->start;
143   ft.chrg.cpMax = f->end;
144   ft.lpstrText = f->needle;
145   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
146   ok(findloc == f->expected_loc,
147       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
148       name, id, f->needle, f->start, f->end, f->flags, findloc);
149   ok(ft.chrgText.cpMin == f->expected_loc,
150       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %ld\n",
151       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
152   ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
153         : f->expected_loc + strlen(f->needle)),
154       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %ld\n",
155       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
156 }
157
158 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
159     int num_tests)
160 {
161   int i;
162
163   for (i = 0; i < num_tests; i++) {
164     if (find[i]._todo_wine) {
165       todo_wine {
166         check_EM_FINDTEXT(hwnd, name, &find[i], i);
167         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
168       }
169     } else {
170         check_EM_FINDTEXT(hwnd, name, &find[i], i);
171         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
172     }
173   }
174 }
175
176 static void test_EM_FINDTEXT(void)
177 {
178   HWND hwndRichEdit = new_richedit(NULL);
179
180   /* Empty rich edit control */
181   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
182       sizeof(find_tests)/sizeof(struct find_s));
183
184   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
185
186   /* Haystack text */
187   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
188       sizeof(find_tests2)/sizeof(struct find_s));
189
190   DestroyWindow(hwndRichEdit);
191 }
192
193 static const struct getline_s {
194   int line;
195   size_t buffer_len;
196   const char *text;
197 } gl[] = {
198   {0, 10, "foo bar\r"},
199   {1, 10, "\r"},
200   {2, 10, "bar\r"},
201   {3, 10, "\r"},
202
203   /* Buffer smaller than line length */
204   {0, 2, "foo bar\r"},
205   {0, 1, "foo bar\r"},
206   {0, 0, "foo bar\r"}
207 };
208
209 static void test_EM_GETLINE(void)
210 {
211   int i;
212   HWND hwndRichEdit = new_richedit(NULL);
213   static const int nBuf = 1024;
214   char dest[1024], origdest[1024];
215   const char text[] = "foo bar\n"
216       "\n"
217       "bar\n";
218
219   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
220
221   memset(origdest, 0xBB, nBuf);
222   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
223   {
224     int nCopied;
225     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
226     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
227     memset(dest, 0xBB, nBuf);
228     *(WORD *) dest = gl[i].buffer_len;
229
230     /* EM_GETLINE appends a "\r\0" to the end of the line
231      * nCopied counts up to and including the '\r' */
232     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
233     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
234        expected_nCopied);
235     /* two special cases since a parameter is passed via dest */
236     if (gl[i].buffer_len == 0)
237       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
238          "buffer_len=0\n");
239     else if (gl[i].buffer_len == 1)
240       ok(dest[0] == gl[i].text[0] && !dest[1] &&
241          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
242     else
243     {
244       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
245          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
246       ok(!strncmp(dest + expected_bytes_written, origdest
247                   + expected_bytes_written, nBuf - expected_bytes_written),
248          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
249     }
250   }
251
252   DestroyWindow(hwndRichEdit);
253 }
254
255 static int get_scroll_pos_y(HWND hwnd)
256 {
257   POINT p = {-1, -1};
258   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
259   ok(p.x != -1 && p.y != -1, "p.x:%ld p.y:%ld\n", p.x, p.y);
260   return p.y;
261 }
262
263 static void move_cursor(HWND hwnd, long charindex)
264 {
265   CHARRANGE cr;
266   cr.cpMax = charindex;
267   cr.cpMin = charindex;
268   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
269 }
270
271 static void line_scroll(HWND hwnd, int amount)
272 {
273   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
274 }
275
276 static void test_EM_SCROLLCARET(void)
277 {
278   int prevY, curY;
279   HWND hwndRichEdit = new_richedit(NULL);
280   const char text[] = "aa\n"
281       "this is a long line of text that should be longer than the "
282       "control's width\n"
283       "cc\n"
284       "dd\n"
285       "ee\n"
286       "ff\n"
287       "gg\n"
288       "hh\n";
289
290   /* Can't verify this */
291   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
292
293   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
294
295   /* Caret above visible window */
296   line_scroll(hwndRichEdit, 3);
297   prevY = get_scroll_pos_y(hwndRichEdit);
298   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
299   curY = get_scroll_pos_y(hwndRichEdit);
300   ok(prevY != curY, "%d == %d\n", prevY, curY);
301
302   /* Caret below visible window */
303   move_cursor(hwndRichEdit, sizeof(text) - 1);
304   line_scroll(hwndRichEdit, -3);
305   prevY = get_scroll_pos_y(hwndRichEdit);
306   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
307   curY = get_scroll_pos_y(hwndRichEdit);
308   ok(prevY != curY, "%d == %d\n", prevY, curY);
309
310   /* Caret in visible window */
311   move_cursor(hwndRichEdit, sizeof(text) - 2);
312   prevY = get_scroll_pos_y(hwndRichEdit);
313   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
314   curY = get_scroll_pos_y(hwndRichEdit);
315   ok(prevY == curY, "%d != %d\n", prevY, curY);
316
317   /* Caret still in visible window */
318   line_scroll(hwndRichEdit, -1);
319   prevY = get_scroll_pos_y(hwndRichEdit);
320   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
321   curY = get_scroll_pos_y(hwndRichEdit);
322   ok(prevY == curY, "%d != %d\n", prevY, curY);
323
324   DestroyWindow(hwndRichEdit);
325 }
326
327 static void test_EM_SETTEXTMODE(void)
328 {
329   HWND hwndRichEdit = new_richedit(NULL);
330   CHARFORMAT2 cf2, cf2test;
331   CHARRANGE cr;
332   int rc = 0;
333
334   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
335   /*Insert text into the control*/
336
337   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
338
339   /*Attempt to change the control to plain text mode*/
340   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
341   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
342
343   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
344   If rich text is pasted, it should have the same formatting as the rest
345   of the text in the control*/
346
347   /*Italicize the text
348   *NOTE: If the default text was already italicized, the test will simply
349   reverse; in other words, it will copy a regular "wine" into a plain
350   text window that uses an italicized format*/
351   cf2.cbSize = sizeof(CHARFORMAT2);
352   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
353              (LPARAM) &cf2);
354
355   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
356   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
357
358   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
359   however, SCF_ALL has been implemented*/
360   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
361   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
362
363   /*Select the string "wine"*/
364   cr.cpMin = 0;
365   cr.cpMax = 4;
366   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
367
368   /*Copy the italicized "wine" to the clipboard*/
369   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
370
371   /*Reset the formatting to default*/
372   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
373   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
374
375   /*Clear the text in the control*/
376   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
377
378   /*Switch to Plain Text Mode*/
379   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
380   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
381
382   /*Input "wine" again in normal format*/
383   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
384
385   /*Paste the italicized "wine" into the control*/
386   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
387
388   /*Select a character from the first "wine" string*/
389   cr.cpMin = 2;
390   cr.cpMax = 3;
391   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
392
393   /*Retrieve its formatting*/
394   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
395               (LPARAM) &cf2);
396
397   /*Select a character from the second "wine" string*/
398   cr.cpMin = 5;
399   cr.cpMax = 6;
400   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
401
402   /*Retrieve its formatting*/
403   cf2test.cbSize = sizeof(CHARFORMAT2);
404   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
405                (LPARAM) &cf2test);
406
407   /*Compare the two formattings*/
408     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
409       "two formats found in plain text mode - cf2.dwEffects: %lx cf2test.dwEffects: %lx\n",
410        cf2.dwEffects, cf2test.dwEffects);
411   /*Test TM_RICHTEXT by: switching back to Rich Text mode
412                          printing "wine" in the current format(normal)
413                          pasting "wine" from the clipboard(italicized)
414                          comparing the two formats(should differ)*/
415
416   /*Attempt to switch with text in control*/
417   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
418   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
419
420   /*Clear control*/
421   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
422
423   /*Switch into Rich Text mode*/
424   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
425   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
426
427   /*Print "wine" in normal formatting into the control*/
428   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
429
430   /*Paste italicized "wine" into the control*/
431   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
432
433   /*Select text from the first "wine" string*/
434   cr.cpMin = 1;
435   cr.cpMax = 3;
436   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
437
438   /*Retrieve its formatting*/
439   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
440                 (LPARAM) &cf2);
441
442   /*Select text from the second "wine" string*/
443   cr.cpMin = 6;
444   cr.cpMax = 7;
445   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
446
447   /*Retrieve its formatting*/
448   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
449                 (LPARAM) &cf2test);
450
451   /*Test that the two formattings are not the same*/
452   todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
453       "expected different formats - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
454       cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
455
456   DestroyWindow(hwndRichEdit);
457 }
458
459 static void test_TM_PLAINTEXT(void)
460 {
461   /*Tests plain text properties*/
462
463   HWND hwndRichEdit = new_richedit(NULL);
464   CHARFORMAT2 cf2, cf2test;
465   CHARRANGE cr;
466
467   /*Switch to plain text mode*/
468
469   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
470   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
471
472   /*Fill control with text*/
473
474   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
475
476   /*Select some text and bold it*/
477
478   cr.cpMin = 10;
479   cr.cpMax = 20;
480   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
481   cf2.cbSize = sizeof(CHARFORMAT2);
482   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
483               (LPARAM) &cf2);
484
485   cf2.dwMask = CFM_BOLD | cf2.dwMask;
486   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
487
488   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
489
490   /*Get the formatting of those characters*/
491
492   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
493
494   /*Get the formatting of some other characters*/
495   cf2test.cbSize = sizeof(CHARFORMAT2);
496   cr.cpMin = 21;
497   cr.cpMax = 30;
498   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
499   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
500
501   /*Test that they are the same as plain text allows only one formatting*/
502
503   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
504      "two selections' formats differ - cf2.dwMask: %lx, cf2test.dwMask %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
505      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
506   
507   /*Fill the control with a "wine" string, which when inserted will be bold*/
508
509   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
510
511   /*Copy the bolded "wine" string*/
512
513   cr.cpMin = 0;
514   cr.cpMax = 4;
515   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
516   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
517
518   /*Swap back to rich text*/
519
520   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
521   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
522
523   /*Set the default formatting to bold italics*/
524
525   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
526   cf2.dwMask |= CFM_ITALIC;
527   cf2.dwEffects ^= CFE_ITALIC;
528   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
529
530   /*Set the text in the control to "wine", which will be bold and italicized*/
531
532   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
533
534   /*Paste the plain text "wine" string, which should take the insert
535    formatting, which at the moment is bold italics*/
536
537   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
538
539   /*Select the first "wine" string and retrieve its formatting*/
540
541   cr.cpMin = 1;
542   cr.cpMax = 3;
543   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
544   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
545
546   /*Select the second "wine" string and retrieve its formatting*/
547
548   cr.cpMin = 5;
549   cr.cpMax = 7;
550   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
551   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
552
553   /*Compare the two formattings. They should be the same.*/
554
555   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
556      "Copied text retained formatting - cf2.dwMask: %lx, cf2test.dwMask: %lx, cf2.dwEffects: %lx, cf2test.dwEffects: %lx\n",
557      cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
558   DestroyWindow(hwndRichEdit);
559 }
560
561 static void test_WM_GETTEXT(void)
562 {
563     HWND hwndRichEdit = new_richedit(NULL);
564     static const char text[] = "Hello. My name is RichEdit!";
565     char buffer[1024] = {0};
566     int result;
567
568     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
569     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
570     result = strcmp(buffer,text);
571     ok(result == 0, 
572         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
573 }
574
575 /* FIXME: need to test unimplemented options and robustly test wparam */
576 static void test_EM_SETOPTIONS(void)
577 {
578     HWND hwndRichEdit = new_richedit(NULL);
579     static const char text[] = "Hello. My name is RichEdit!";
580     char buffer[1024] = {0};
581
582     /* NEGATIVE TESTING - NO OPTIONS SET */
583     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
584     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
585
586     /* testing no readonly by sending 'a' to the control*/
587     SetFocus(hwndRichEdit);
588     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
589     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
590     ok(buffer[0]=='a', 
591        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
592     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
593
594     /* READONLY - sending 'a' to the control */
595     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
596     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
597     SetFocus(hwndRichEdit);
598     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
599     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
600     ok(buffer[0]==text[0], 
601        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
602
603     DestroyWindow(hwndRichEdit);
604 }
605
606 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
607 {
608   CHARFORMAT2W text_format;
609   int link_present = 0;
610   text_format.cbSize = sizeof(text_format);
611   SendMessage(hwnd, EM_SETSEL, 0, 0);
612   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
613   link_present = text_format.dwEffects & CFE_LINK;
614   if (is_url) 
615   { /* control text is url; should get CFE_LINK */
616         ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
617   }
618   else 
619   {
620     ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
621   }
622 }
623
624 static HWND new_static_wnd(HWND parent) {
625   return new_window("Static", 0, parent);
626 }
627
628 static void test_EM_AUTOURLDETECT(void)
629 {
630   struct urls_s {
631     const char *text;
632     int is_url;
633   } urls[12] = {
634     {"winehq.org", 0},
635     {"http://www.winehq.org", 1},
636     {"http//winehq.org", 0},
637     {"ww.winehq.org", 0},
638     {"www.winehq.org", 1},
639     {"ftp://192.168.1.1", 1},
640     {"ftp//192.168.1.1", 0},
641     {"mailto:your@email.com", 1},    
642     {"prospero:prosperoserver", 1},
643     {"telnet:test", 1},
644     {"news:newserver", 1},
645     {"wais:waisserver", 1}  
646   };
647
648   int i;
649   int urlRet=-1;
650   HWND hwndRichEdit, parent;
651
652   parent = new_static_wnd(NULL);
653   hwndRichEdit = new_richedit(parent);
654   /* Try and pass EM_AUTOURLDETECT some test wParam values */
655   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
656   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
657   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
658   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
659   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
660   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
661   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
662   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
663   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
664   /* for each url, check the text to see if CFE_LINK effect is present */
665   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
666     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
667     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
668     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
669     check_CFE_LINK_rcvd(hwndRichEdit, 0);
670     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
671     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
672     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
673     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
674   }
675   DestroyWindow(hwndRichEdit);
676   DestroyWindow(parent);
677 }
678
679 static void test_EM_SCROLL(void)
680 {
681   int i, j;
682   int r; /* return value */
683   int expr; /* expected return value */
684   HWND hwndRichEdit = new_richedit(NULL);
685   int y_before, y_after; /* units of lines of text */
686
687   /* test a richedit box containing a single line of text */
688   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
689   expr = 0x00010000;
690   for (i = 0; i < 4; i++) {
691     static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
692
693     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
694     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
695     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
696        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
697     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
698        "(i == %d)\n", y_after, i);
699   }
700
701   /*
702    * test a richedit box that will scroll. There are two general
703    * cases: the case without any long lines and the case with a long
704    * line.
705    */
706   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
707     if (i == 0)
708       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
709     else
710       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
711                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
712                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
713                   "LONG LINE \nb\nc\nd\ne");
714     for (j = 0; j < 12; j++) /* reset scrol position to top */
715       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
716
717     /* get first visible line */
718     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
719     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
720
721     /* get new current first visible line */
722     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
723
724     ok(((r & 0xffffff00) == 0x00010000) &&
725        ((r & 0x000000ff) != 0x00000000),
726        "EM_SCROLL page down didn't scroll by a small positive number of "
727        "lines (r == 0x%08x)\n", r);
728     ok(y_after > y_before, "EM_SCROLL page down not functioning "
729        "(line %d scrolled to line %d\n", y_before, y_after);
730
731     y_before = y_after;
732     
733     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
734     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
735     ok(((r & 0xffffff00) == 0x0001ff00),
736        "EM_SCROLL page up didn't scroll by a small negative number of lines "
737        "(r == 0x%08x)\n", r);
738     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
739        "%d scrolled to line %d\n", y_before, y_after);
740     
741     y_before = y_after;
742
743     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
744
745     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
746
747     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
748        "(r == 0x%08x)\n", r);
749     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
750        "1 line (%d scrolled to %d)\n", y_before, y_after);
751
752     y_before = y_after;
753
754     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
755
756     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
757
758     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
759        "(r == 0x%08x)\n", r);
760     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
761        "line (%d scrolled to %d)\n", y_before, y_after);
762
763     y_before = y_after;
764
765     r = SendMessage(hwndRichEdit, EM_SCROLL,
766                     SB_LINEUP, 0); /* lineup beyond top */
767
768     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
769
770     ok(r == 0x00010000,
771        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
772     ok(y_before == y_after,
773        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
774
775     y_before = y_after;
776
777     r = SendMessage(hwndRichEdit, EM_SCROLL,
778                     SB_PAGEUP, 0);/*page up beyond top */
779
780     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
781
782     ok(r == 0x00010000,
783        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
784     ok(y_before == y_after,
785        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
786
787     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
788       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
789     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
790     r = SendMessage(hwndRichEdit, EM_SCROLL,
791                     SB_PAGEDOWN, 0); /* page down beyond bot */
792     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
793
794     ok(r == 0x00010000,
795        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
796     ok(y_before == y_after,
797        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
798        y_before, y_after);
799
800     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
801     SendMessage(hwndRichEdit, EM_SCROLL,
802                 SB_LINEDOWN, 0); /* line down beyond bot */
803     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
804     
805     ok(r == 0x00010000,
806        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
807     ok(y_before == y_after,
808        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
809        y_before, y_after);
810   }
811   DestroyWindow(hwndRichEdit);
812 }
813
814 static void test_EM_SETUNDOLIMIT(void)
815 {
816   /* cases we test for:
817    * default behaviour - limiting at 100 undo's 
818    * undo disabled - setting a limit of 0
819    * undo limited -  undo limit set to some to some number, like 2
820    * bad input - sending a negative number should default to 100 undo's */
821  
822   HWND hwndRichEdit = new_richedit(NULL);
823   CHARRANGE cr;
824   int i;
825   int result;
826   
827   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
828   cr.cpMin = 0;
829   cr.cpMax = 1;
830   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
831     /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
832       also, multiple pastes don't combine like WM_CHAR would */
833   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
834
835   /* first case - check the default */
836   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
837   for (i=0; i<101; i++) /* Put 101 undo's on the stack */
838     SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
839   for (i=0; i<100; i++) /* Undo 100 of them */
840     SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
841   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
842      "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
843
844   /* second case - cannot undo */
845   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
846   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); 
847   SendMessage(hwndRichEdit,
848               WM_PASTE, 0, 0); /* Try to put something in the undo stack */
849   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
850      "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
851
852   /* third case - set it to an arbitrary number */
853   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); 
854   SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); 
855   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
856   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
857   SendMessage(hwndRichEdit, WM_PASTE, 0, 0); 
858   /* If SETUNDOLIMIT is working, there should only be two undo's after this */
859   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
860      "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
861   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
862   ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
863      "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
864   SendMessage(hwndRichEdit, WM_UNDO, 0, 0); 
865   ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
866      "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
867   
868   /* fourth case - setting negative numbers should default to 100 undos */
869   SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); 
870   result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
871   ok (result == 100, 
872       "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
873       
874   DestroyWindow(hwndRichEdit);
875 }
876
877 static void test_ES_PASSWORD()
878 {
879   /* This isn't hugely testable, so we're just going to run it through it's paces. */
880         
881   HWND hwndRichEdit = new_richedit(NULL);
882   WCHAR result;
883         
884   /* First, check the default of a regular control */
885   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
886   ok (result == 0,
887         "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
888   
889   /* Now, set it to something normal */
890   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
891   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
892   ok (result == 120,
893         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
894         
895   /* Now, set it to something odd */    
896   SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
897   result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
898   ok (result == 1234,
899         "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
900 }
901
902 static void test_EM_SETTEXTEX()
903 {
904   HWND hwndRichEdit = new_richedit(NULL);
905   SETTEXTEX setText;
906   GETTEXTEX getText;
907   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
908                        'S', 'o', 'm', 'e', 
909                        'T', 'e', 'x', 't', 0}; 
910 #define MAX_BUF_LEN 1024
911   WCHAR buf[MAX_BUF_LEN];
912   int result;
913   CHARRANGE cr;
914
915   setText.codepage = 1200;  /* no constant for unicode */
916   getText.codepage = 1200;  /* no constant for unicode */
917   getText.cb = MAX_BUF_LEN;
918   getText.flags = GT_DEFAULT;
919
920   setText.flags = 0;
921   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
922   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
923   ok(lstrcmpW(buf, TestItem1) == 0,
924       "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
925
926   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
927                        (WPARAM)&setText, (LPARAM) NULL);
928   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
929   
930   ok (result == 1, 
931       "EM_SETTEXTEX returned %d, instead of 1\n",result);
932   ok(lstrlenW(buf) == 0,
933       "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
934   
935   /* put some text back */
936   setText.flags = 0;
937   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
938   /* select some text */
939   cr.cpMax = 1;
940   cr.cpMin = 3;
941   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
942   /* replace current selection */
943   setText.flags = ST_SELECTION;
944   result = SendMessage(hwndRichEdit, EM_SETTEXTEX, 
945                        (WPARAM)&setText, (LPARAM) NULL);
946   ok(result == 0,
947       "EM_SETTEXTEX with NULL lParam to replace selection"
948       " with no text should return 0. Got %i\n",
949       result);
950   
951   /* put some text back */
952   setText.flags = 0;
953   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
954   /* select some text */
955   cr.cpMax = 1;
956   cr.cpMin = 3;
957   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
958   /* replace current selection */
959   setText.flags = ST_SELECTION;
960   result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
961                        (WPARAM)&setText, (LPARAM) TestItem1);
962   /* get text */
963   SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
964   ok(result == lstrlenW(TestItem1),
965       "EM_SETTEXTEX with NULL lParam to replace selection"
966       " with no text should return 0. Got %i\n",
967       result);
968   ok(lstrlenW(buf) == 22,
969       "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
970       lstrlenW(buf) );
971
972   DestroyWindow(hwndRichEdit);
973 }
974
975
976 static void test_EM_EXLIMITTEXT(void)
977 {
978   int i, selBegin, selEnd, len1, len2;
979   int BUFSIZE = 1024;
980   char text[BUFSIZE+1];
981   int textlimit = 0; /* multiple of 100 */
982   HWND hwndRichEdit = new_richedit(NULL);
983   
984   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
985   ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
986   
987   textlimit = 256000;
988   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
989   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
990   /* set higher */
991   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
992   
993   textlimit = 1000;
994   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
995   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
996   /* set lower */
997   ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
998  
999   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
1000   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1001   /* default for WParam = 0 */
1002   ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
1003  
1004   textlimit = BUFSIZE;
1005   memset(text, 'W', textlimit);
1006   text[BUFSIZE] = 0;
1007   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
1008   /* maxed out text */
1009   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1010   
1011   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);  /* select everything */
1012   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1013   len1 = selEnd - selBegin;
1014   
1015   SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
1016   SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
1017   SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
1018   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1019   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1020   len2 = selEnd - selBegin;
1021   
1022   ok(len1 != len2,
1023     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1024     len1,len2,i);
1025   
1026   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1027   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1028   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
1029   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1030   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1031   len1 = selEnd - selBegin;
1032   
1033   ok(len1 != len2,
1034     "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1035     len1,len2,i);
1036   
1037   SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
1038   SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
1039   SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);  /* full; should be no effect */
1040   SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
1041   SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
1042   len2 = selEnd - selBegin;
1043   
1044   ok(len1 == len2, 
1045     "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
1046     len1,len2,i);
1047
1048   DestroyWindow(hwndRichEdit);
1049 }
1050
1051 static void test_EM_GETLIMITTEXT(void)
1052 {
1053   int i;
1054   HWND hwndRichEdit = new_richedit(NULL);
1055
1056   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1057   ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
1058
1059   SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
1060   i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
1061   ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
1062
1063   DestroyWindow(hwndRichEdit);
1064 }
1065
1066 static void test_WM_SETFONT()
1067 {
1068   /* There is no invalid input or error conditions for this function.
1069    * NULL wParam and lParam just fall back to their default values 
1070    * It should be noted that even if you use a gibberish name for your fonts
1071    * here, it will still work because the name is stored. They will display as
1072    * System, but will report their name to be whatever they were created as */
1073   
1074   HWND hwndRichEdit = new_richedit(NULL);
1075   HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1076     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1077     FF_DONTCARE, "Marlett");
1078   HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1079     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1080     FF_DONTCARE, "MS Sans Serif");
1081   HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1082     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1083     FF_DONTCARE, "Courier");
1084   LOGFONTA sentLogFont;
1085   CHARFORMAT2A returnedCF2A;
1086   
1087   returnedCF2A.cbSize = sizeof(returnedCF2A);
1088   
1089   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
1090   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
1091   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1092
1093   GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
1094   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1095     "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
1096     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1097
1098   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
1099   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1100   GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
1101   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1102     "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
1103     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1104     
1105   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
1106   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1107   GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
1108   ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
1109     "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
1110     sentLogFont.lfFaceName,returnedCF2A.szFaceName);
1111    
1112   /* This last test is special since we send in NULL. We clear the variables
1113    * and just compare to "System" instead of the sent in font name. */
1114   ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
1115   ZeroMemory(&sentLogFont,sizeof(sentLogFont));
1116   returnedCF2A.cbSize = sizeof(returnedCF2A);
1117   
1118   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
1119   SendMessage(hwndRichEdit, EM_GETCHARFORMAT,   SCF_DEFAULT,  (LPARAM) &returnedCF2A);
1120   GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
1121   ok (!strcmp("System",returnedCF2A.szFaceName),
1122     "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
1123   
1124   DestroyWindow(hwndRichEdit);
1125 }
1126
1127
1128 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
1129                                          LPBYTE pbBuff,
1130                                          LONG cb,
1131                                          LONG *pcb)
1132 {
1133   const char** str = (const char**)dwCookie;
1134   int size = strlen(*str);
1135   if(size > 3)  /* let's make it peice-meal for fun */
1136     size = 3;
1137   *pcb = cb;
1138   if (*pcb > size) {
1139     *pcb = size;
1140   }
1141   if (*pcb > 0) {
1142     memcpy(pbBuff, *str, *pcb);
1143     *str += *pcb;
1144   }
1145   return 0;
1146 }
1147
1148 static void test_EM_GETMODIFY(void)
1149 {
1150   HWND hwndRichEdit = new_richedit(NULL);
1151   LRESULT result;
1152   SETTEXTEX setText;
1153   WCHAR TestItem1[] = {'T', 'e', 's', 't', 
1154                        'S', 'o', 'm', 'e', 
1155                        'T', 'e', 'x', 't', 0}; 
1156   WCHAR TestItem2[] = {'T', 'e', 's', 't', 
1157                        'S', 'o', 'm', 'e', 
1158                        'O', 't', 'h', 'e', 'r',
1159                        'T', 'e', 'x', 't', 0}; 
1160   const char* streamText = "hello world";
1161   CHARFORMAT2 cf2;
1162   PARAFORMAT2 pf2;
1163   EDITSTREAM es;
1164   
1165   HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, 
1166     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | 
1167     FF_DONTCARE, "Courier");
1168   
1169   setText.codepage = 1200;  /* no constant for unicode */
1170   setText.flags = ST_KEEPUNDO;
1171   
1172
1173   /* modify flag shouldn't be set when richedit is first created */
1174   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1175   ok (result == 0, 
1176       "EM_GETMODIFY returned non-zero, instead of zero on create\n");
1177   
1178   /* setting modify flag should actually set it */
1179   SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
1180   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1181   ok (result != 0, 
1182       "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
1183   
1184   /* clearing modify flag should actually clear it */
1185   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1186   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1187   ok (result == 0, 
1188       "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
1189  
1190   /* setting font doesn't change modify flag */
1191   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1192   SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
1193   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1194   todo_wine {
1195   ok (result == 0,
1196       "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
1197   }
1198
1199   /* setting text should set modify flag */
1200   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1201   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1202   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1203   ok (result != 0,
1204       "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
1205   
1206   /* undo previous text doesn't reset modify flag */
1207   SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
1208   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1209   todo_wine {
1210   ok (result != 0,
1211       "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
1212   }
1213   
1214   /* set text with no flag to keep undo stack should not set modify flag */
1215   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1216   setText.flags = 0;
1217   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1218   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1219   todo_wine {
1220   ok (result == 0,
1221       "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
1222   }
1223   
1224   /* WM_SETTEXT doesn't modify */
1225   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1226   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
1227   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1228   todo_wine {
1229   ok (result == 0,
1230       "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
1231   }
1232   
1233   /* clear the text */
1234   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1235   SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
1236   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1237   ok (result == 0,
1238       "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
1239   
1240   /* replace text */
1241   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1242   SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
1243   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1244   SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
1245   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1246   ok (result != 0,
1247       "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
1248   
1249   /* copy/paste text 1 */
1250   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1251   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1252   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1253   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1254   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1255   ok (result != 0,
1256       "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
1257   
1258   /* copy/paste text 2 */
1259   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1260   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
1261   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1262   SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
1263   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1264   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1265   ok (result != 0,
1266       "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
1267   
1268   /* press char */
1269   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1270   SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
1271   SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
1272   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1273   ok (result != 0,
1274       "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
1275   
1276   /* set char format */
1277   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1278   cf2.cbSize = sizeof(CHARFORMAT2);
1279   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1280              (LPARAM) &cf2);
1281   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1282   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1283   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1284   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1285   ok (result != 0,
1286       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
1287   
1288   /* set para format */
1289   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1290   pf2.cbSize = sizeof(PARAFORMAT2);
1291   SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
1292              (LPARAM) &pf2);
1293   pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
1294   pf2.wAlignment = PFA_RIGHT;
1295   SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
1296   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1297   todo_wine {
1298   ok (result == 0,
1299       "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
1300   }
1301
1302   /* EM_STREAM */
1303   SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
1304   es.dwCookie = (DWORD_PTR)&streamText;
1305   es.dwError = 0;
1306   es.pfnCallback = test_EM_GETMODIFY_esCallback;
1307   SendMessage(hwndRichEdit, EM_STREAMIN, 
1308               (WPARAM)(SF_TEXT), (LPARAM)&es);
1309   result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1310   todo_wine {
1311   ok (result != 0,
1312       "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
1313   }
1314
1315   
1316   DestroyWindow(hwndRichEdit);
1317 }
1318
1319 START_TEST( editor )
1320 {
1321   MSG msg;
1322   time_t end;
1323
1324   /* Must explicitly LoadLibrary(). The test has no references to functions in
1325    * RICHED20.DLL, so the linker doesn't actually link to it. */
1326   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
1327   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1328   test_EM_FINDTEXT();
1329   test_EM_GETLINE();
1330   test_EM_SCROLLCARET();
1331   test_EM_SCROLL();
1332   test_EM_SETTEXTMODE();
1333   test_TM_PLAINTEXT();
1334   test_EM_SETOPTIONS();
1335   test_WM_GETTEXT();
1336   test_EM_AUTOURLDETECT();
1337   test_EM_SETUNDOLIMIT();
1338   test_ES_PASSWORD();
1339   test_EM_SETTEXTEX();
1340   test_EM_EXLIMITTEXT();
1341   test_EM_GETLIMITTEXT();
1342   test_WM_SETFONT();
1343   test_EM_GETMODIFY();
1344
1345   /* Set the environment variable WINETEST_RICHED20 to keep windows
1346    * responsive and open for 30 seconds. This is useful for debugging.
1347    *
1348    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1349    * 50ms before retrying the queue. */
1350   end = time(NULL) + 30;
1351   if (getenv( "WINETEST_RICHED20" )) {
1352     while (time(NULL) < end) {
1353       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1354         TranslateMessage(&msg);
1355         DispatchMessage(&msg);
1356       } else {
1357         Sleep(50);
1358       }
1359     }
1360   }
1361
1362   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
1363 }