riched20: Implement EM_AUTOURLDETECT & EM_GETAUTOURLDETECT.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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   char *needle;
48   int flags;
49   int expected_loc;
50   int _todo_wine;
51 };
52
53 struct find_s find_tests[] = {
54   /* Find in empty text */
55   {0, -1, "foo", FR_DOWN, -1, 0},
56   {0, -1, "foo", 0, -1, 0},
57   {0, -1, "", FR_DOWN, -1, 0},
58   {20, 5, "foo", FR_DOWN, -1, 0},
59   {5, 20, "foo", FR_DOWN, -1, 0}
60 };
61
62 struct find_s find_tests2[] = {
63   /* No-result find */
64   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
65   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
66
67   /* Subsequent finds */
68   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
69   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
70   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
71   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
72
73   /* Find backwards */
74   {19, 20, "Wine", FR_MATCHCASE, 13, 0},
75   {10, 20, "Wine", FR_MATCHCASE, 4, 0},
76   {20, 10, "Wine", FR_MATCHCASE, 13, 0},
77
78   /* Case-insensitive */
79   {1, 31, "wInE", FR_DOWN, 4, 0},
80   {1, 31, "Wine", FR_DOWN, 4, 0},
81
82   /* High-to-low ranges */
83   {20, 5, "Wine", FR_DOWN, -1, 0},
84   {2, 1, "Wine", FR_DOWN, -1, 0},
85   {30, 29, "Wine", FR_DOWN, -1, 0},
86   {20, 5, "Wine", 0, 13, 0},
87
88   /* Find nothing */
89   {5, 10, "", FR_DOWN, -1, 0},
90   {10, 5, "", FR_DOWN, -1, 0},
91   {0, -1, "", FR_DOWN, -1, 0},
92   {10, 5, "", 0, -1, 0},
93
94   /* Whole-word search */
95   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 1},
96   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 1},
97   
98   /* Bad ranges */
99   {-20, 20, "Wine", FR_DOWN, -1, 0},
100   {-20, 20, "Wine", FR_DOWN, -1, 0},
101   {-15, -20, "Wine", FR_DOWN, -1, 0},
102   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
103
104   /* Check the case noted in bug 4479 where matches at end aren't recognized */
105   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
106   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
107   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
108   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
109   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
110
111   /* The backwards case of bug 4479; bounds look right
112    * Fails because backward find is wrong */
113   {19, 20, "WINE", FR_MATCHCASE, 0, 0},
114   {0, 20, "WINE", FR_MATCHCASE, -1, 0}
115 };
116
117 static void check_EM_FINDTEXT(HWND hwnd, char *name, struct find_s *f, int id) {
118   int findloc;
119   FINDTEXT ft;
120   memset(&ft, 0, sizeof(ft));
121   ft.chrg.cpMin = f->start;
122   ft.chrg.cpMax = f->end;
123   ft.lpstrText = f->needle;
124   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
125   ok(findloc == f->expected_loc,
126      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
127      name, id, f->needle, f->start, f->end, f->flags, findloc);
128 }
129
130 static void check_EM_FINDTEXTEX(HWND hwnd, char *name, struct find_s *f,
131     int id) {
132   int findloc;
133   FINDTEXTEX ft;
134   memset(&ft, 0, sizeof(ft));
135   ft.chrg.cpMin = f->start;
136   ft.chrg.cpMax = f->end;
137   ft.lpstrText = f->needle;
138   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
139   ok(findloc == f->expected_loc,
140       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
141       name, id, f->needle, f->start, f->end, f->flags, findloc);
142   ok(ft.chrgText.cpMin == f->expected_loc,
143       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %ld\n",
144       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
145   ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
146         : f->expected_loc + strlen(f->needle)),
147       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %ld\n",
148       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
149 }
150
151 static void run_tests_EM_FINDTEXT(HWND hwnd, char *name, struct find_s *find,
152     int num_tests)
153 {
154   int i;
155
156   for (i = 0; i < num_tests; i++) {
157     if (find[i]._todo_wine) {
158       todo_wine {
159         check_EM_FINDTEXT(hwnd, name, &find[i], i);
160         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
161       }
162     } else {
163         check_EM_FINDTEXT(hwnd, name, &find[i], i);
164         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
165     }
166   }
167 }
168
169 static void test_EM_FINDTEXT(void)
170 {
171   HWND hwndRichEdit = new_richedit(NULL);
172
173   /* Empty rich edit control */
174   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
175       sizeof(find_tests)/sizeof(struct find_s));
176
177   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
178
179   /* Haystack text */
180   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
181       sizeof(find_tests2)/sizeof(struct find_s));
182
183   DestroyWindow(hwndRichEdit);
184 }
185
186 static int get_scroll_pos_y(HWND hwnd)
187 {
188   POINT p = {-1, -1};
189   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
190   ok(p.x != -1 && p.y != -1, "p.x:%ld p.y:%ld\n", p.x, p.y);
191   return p.y;
192 }
193
194 static void move_cursor(HWND hwnd, long charindex)
195 {
196   CHARRANGE cr;
197   cr.cpMax = charindex;
198   cr.cpMin = charindex;
199   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
200 }
201
202 static void line_scroll(HWND hwnd, int amount)
203 {
204   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
205 }
206
207 static void test_EM_SCROLLCARET(void)
208 {
209   int prevY, curY;
210   HWND hwndRichEdit = new_richedit(NULL);
211   const char text[] = "aa\n"
212       "this is a long line of text that should be longer than the "
213       "control's width\n"
214       "cc\n"
215       "dd\n"
216       "ee\n"
217       "ff\n"
218       "gg\n"
219       "hh\n";
220
221   /* Can't verify this */
222   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
223
224   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
225
226   /* Caret above visible window */
227   line_scroll(hwndRichEdit, 3);
228   prevY = get_scroll_pos_y(hwndRichEdit);
229   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
230   curY = get_scroll_pos_y(hwndRichEdit);
231   ok(prevY != curY, "%d == %d\n", prevY, curY);
232
233   /* Caret below visible window */
234   move_cursor(hwndRichEdit, sizeof(text) - 1);
235   line_scroll(hwndRichEdit, -3);
236   prevY = get_scroll_pos_y(hwndRichEdit);
237   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
238   curY = get_scroll_pos_y(hwndRichEdit);
239   ok(prevY != curY, "%d == %d\n", prevY, curY);
240
241   /* Caret in visible window */
242   move_cursor(hwndRichEdit, sizeof(text) - 2);
243   prevY = get_scroll_pos_y(hwndRichEdit);
244   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
245   curY = get_scroll_pos_y(hwndRichEdit);
246   ok(prevY == curY, "%d != %d\n", prevY, curY);
247
248   /* Caret still in visible window */
249   line_scroll(hwndRichEdit, -1);
250   prevY = get_scroll_pos_y(hwndRichEdit);
251   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
252   curY = get_scroll_pos_y(hwndRichEdit);
253   ok(prevY == curY, "%d != %d\n", prevY, curY);
254
255   DestroyWindow(hwndRichEdit);
256 }
257
258 static void test_EM_SETTEXTMODE(void)
259 {
260   HWND hwndRichEdit = new_richedit(NULL);
261   CHARFORMAT2 cf2, cf2test;
262   CHARRANGE cr;
263   int rc = 0;
264
265   /*Test that EM_SETTEXTMODE fails if text exists within the control*/
266   /*Insert text into the control*/
267
268   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
269
270   /*Attempt to change the control to plain text mode*/
271   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
272   ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
273
274   /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
275   If rich text is pasted, it should have the same formatting as the rest
276   of the text in the control*/
277
278   /*Italicize the text
279   *NOTE: If the default text was already italicized, the test will simply
280   reverse; in other words, it will copy a regular "wine" into a plain
281   text window that uses an italicized format*/
282   cf2.cbSize = sizeof(CHARFORMAT2);
283   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
284              (LPARAM) &cf2);
285
286   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
287   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
288
289   /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
290   however, SCF_ALL has been implemented*/
291   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
292   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
293
294   /*Select the string "wine"*/
295   cr.cpMin = 0;
296   cr.cpMax = 4;
297   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
298
299   /*Copy the italicized "wine" to the clipboard*/
300   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
301
302   /*Reset the formatting to default*/
303   cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
304   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
305
306   /*Clear the text in the control*/
307   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
308
309   /*Switch to Plain Text Mode*/
310   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
311   ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control:  returned: %d\n", rc);
312
313   /*Input "wine" again in normal format*/
314   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
315
316   /*Paste the italicized "wine" into the control*/
317   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
318
319   /*Select a character from the first "wine" string*/
320   cr.cpMin = 2;
321   cr.cpMax = 3;
322   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
323
324   /*Retrieve its formatting*/
325   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
326               (LPARAM) &cf2);
327
328   /*Select a character from the second "wine" string*/
329   cr.cpMin = 5;
330   cr.cpMax = 6;
331   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
332
333   /*Retrieve its formatting*/
334   cf2test.cbSize = sizeof(CHARFORMAT2);
335   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
336                (LPARAM) &cf2test);
337
338   /*Compare the two formattings*/
339     ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
340       "two formats found in plain text mode - cf2.dwEffects: %f cf2test.dwEffects: %f\n",(double) cf2.dwEffects, (double) cf2test.dwEffects);
341   /*Test TM_RICHTEXT by: switching back to Rich Text mode
342                          printing "wine" in the current format(normal)
343                          pasting "wine" from the clipboard(italicized)
344                          comparing the two formats(should differ)*/
345
346   /*Attempt to switch with text in control*/
347   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
348   ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
349
350   /*Clear control*/
351   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
352
353   /*Switch into Rich Text mode*/
354   rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
355   ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
356
357   /*Print "wine" in normal formatting into the control*/
358   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
359
360   /*Paste italicized "wine" into the control*/
361   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
362
363   /*Select text from the first "wine" string*/
364   cr.cpMin = 1;
365   cr.cpMax = 3;
366   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
367
368   /*Retrieve its formatting*/
369   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
370                 (LPARAM) &cf2);
371
372   /*Select text from the second "wine" string*/
373   cr.cpMin = 6;
374   cr.cpMax = 7;
375   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
376
377   /*Retrieve its formatting*/
378   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
379                 (LPARAM) &cf2test);
380
381   /*Test that the two formattings are not the same*/
382   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
383       "expected different formats - cf2.dwMask: %f, cf2test.dwMask: %f, cf2.dwEffects: %f, cf2test.dwEffects: %f\n",
384       (double) cf2.dwMask, (double) cf2test.dwMask, (double) cf2.dwEffects, (double) cf2test.dwEffects);
385
386   DestroyWindow(hwndRichEdit);
387 }
388
389 static void test_TM_PLAINTEXT()
390 {
391   /*Tests plain text properties*/
392
393   HWND hwndRichEdit = new_richedit(NULL);
394   CHARFORMAT2 cf2, cf2test;
395   CHARRANGE cr;
396
397   /*Switch to plain text mode*/
398
399   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
400   SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
401
402   /*Fill control with text*/
403
404   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
405
406   /*Select some text and bold it*/
407
408   cr.cpMin = 10;
409   cr.cpMax = 20;
410   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
411   cf2.cbSize = sizeof(CHARFORMAT2);
412   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
413               (LPARAM) &cf2);
414
415   cf2.dwMask = CFM_BOLD | cf2.dwMask;
416   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
417
418   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
419
420   /*Get the formatting of those characters*/
421
422   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
423
424   /*Get the formatting of some other characters*/
425   cf2test.cbSize = sizeof(CHARFORMAT2);
426   cr.cpMin = 21;
427   cr.cpMax = 30;
428   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
429   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
430
431   /*Test that they are the same as plain text allows only one formatting*/
432
433   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
434      "two selections' formats differ - cf2.dwMask: %f, cf2test.dwMask %f, cf2.dwEffects: %f, cf2test.dwEffects: %f\n",
435      (double) cf2.dwMask, (double) cf2test.dwMask, (double) cf2.dwEffects, (double) cf2test.dwEffects);
436   
437   /*Fill the control with a "wine" string, which when inserted will be bold*/
438
439   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
440
441   /*Copy the bolded "wine" string*/
442
443   cr.cpMin = 0;
444   cr.cpMax = 4;
445   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
446   SendMessage(hwndRichEdit, WM_COPY, 0, 0);
447
448   /*Swap back to rich text*/
449
450   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
451   SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
452
453   /*Set the default formatting to bold italics*/
454
455   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
456   cf2.dwMask |= CFM_ITALIC;
457   cf2.dwEffects ^= CFE_ITALIC;
458   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
459
460   /*Set the text in the control to "wine", which will be bold and italicized*/
461
462   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
463
464   /*Paste the plain text "wine" string, which should take the insert
465    formatting, which at the moment is bold italics*/
466
467   SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
468
469   /*Select the first "wine" string and retrieve its formatting*/
470
471   cr.cpMin = 1;
472   cr.cpMax = 3;
473   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
474   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
475
476   /*Select the second "wine" string and retrieve its formatting*/
477
478   cr.cpMin = 5;
479   cr.cpMax = 7;
480   SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
481   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
482
483   /*Compare the two formattings. They should be the same.*/
484
485   ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
486      "Copied text retained formatting - cf2.dwMask: %f, cf2test.dwMask: %f, cf2.dwEffects: %f, cf2test.dwEffects: %f\n",
487      (double) cf2.dwMask, (double) cf2test.dwMask, (double) cf2.dwEffects, (double) cf2test.dwEffects);
488   DestroyWindow(hwndRichEdit);
489 }
490
491 /* FIXME: Extra '\r' appended at end of gotten text*/
492 static void test_WM_GETTEXT()
493 {
494     HWND hwndRichEdit = new_richedit(NULL);
495     static const char text[] = "Hello. My name is RichEdit!";
496     char buffer[1024] = {0};
497     int result;
498
499     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
500     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
501     result = strcmp(buffer,text);
502     todo_wine{
503       ok(result == 0, 
504         "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
505     }
506 }
507
508 /* FIXME: need to test unimplemented options and robustly test wparam */
509 static void test_EM_SETOPTIONS()
510 {
511     HWND hwndRichEdit = new_richedit(NULL);
512     static const char text[] = "Hello. My name is RichEdit!";
513     char buffer[1024] = {0};
514
515     /* NEGATIVE TESTING - NO OPTIONS SET */
516     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
517     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
518
519     /* testing no readonly by sending 'a' to the control*/
520     SetFocus(hwndRichEdit);
521     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
522     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
523     ok(buffer[0]=='a', 
524        "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
525     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
526
527     /* READONLY - sending 'a' to the control */
528     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
529     SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
530     SetFocus(hwndRichEdit);
531     SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
532     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
533     ok(buffer[0]==text[0], 
534        "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); 
535
536     DestroyWindow(hwndRichEdit);
537 }
538
539 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url)
540 {
541   CHARFORMAT2W text_format;
542   int link_present = 0;
543   text_format.cbSize = sizeof(text_format);
544   SendMessage(hwnd, EM_SETSEL, 0, 0);
545   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
546   link_present = text_format.dwEffects & CFE_LINK;
547   if (is_url) 
548   { /* control text is url; should get CFE_LINK */
549         ok(0 != link_present, "URL Case: CFE_LINK not set.\n");
550   }
551   else 
552   {
553     ok(0 == link_present, "Non-URL Case: CFE_LINK set.\n");
554   }
555 }
556
557 static HWND new_static_wnd(HWND parent) {
558   return new_window("Static", 0, parent);
559 }
560
561 static void test_EM_AUTOURLDETECT(void)
562 {
563   struct urls_s {
564     char *text;
565     int is_url;
566   } urls[12] = {
567     {"winehq.org", 0},
568     {"http://www.winehq.org", 1},
569     {"http//winehq.org", 0},
570     {"ww.winehq.org", 0},
571     {"www.winehq.org", 1},
572     {"ftp://192.168.1.1", 1},
573     {"ftp//192.168.1.1", 0},
574     {"mailto:your@email.com", 1},    
575     {"prospero:prosperoserver", 1},
576     {"telnet:test", 1},
577     {"news:newserver", 1},
578     {"wais:waisserver", 1}  
579   };
580
581   int i;
582   int urlRet=-1;
583   HWND hwndRichEdit, parent;
584
585   parent = new_static_wnd(NULL);
586   hwndRichEdit = new_richedit(parent);
587   /* Try and pass EM_AUTOURLDETECT some test wParam values */
588   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
589   ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
590   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
591   ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
592   /* Windows returns -2147024809 (0x80070057) on bad wParam values */
593   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
594   ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
595   urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
596   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
597   /* for each url, check the text to see if CFE_LINK effect is present */
598   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
599     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
600     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
601     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
602     check_CFE_LINK_rcvd(hwndRichEdit, 0);
603     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
604     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
605     SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
606     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url);
607   }
608   DestroyWindow(hwndRichEdit);
609   DestroyWindow(parent);
610 }
611
612 static void test_EM_SCROLL()
613 {
614   int i, j;
615   int r; /* return value */
616   int expr; /* expected return value */
617   HWND hwndRichEdit = new_richedit(NULL);
618   int y_before, y_after; /* units of lines of text */
619
620   /* test a richedit box containing a single line of text */
621   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
622   expr = 0x00010000;
623   for (i = 0; i < 4; i++) {
624     int cmd;
625     switch (i) {
626     case 0: cmd = SB_PAGEDOWN; break;
627     case 1: cmd = SB_PAGEUP; break;
628     case 2: cmd = SB_LINEDOWN; break;
629     case 3: cmd = SB_LINEUP; break;
630     }
631     
632     r = SendMessage(hwndRichEdit, EM_SCROLL, cmd, 0);
633     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
634     ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
635        "Got 0x%08x, expected 0x%08x\n", i, r, expr);
636     ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
637        "(i == %d)\n", y_after, i);
638   }
639
640   /*
641    * test a richedit box that will scroll. There are two general
642    * cases: the case without any long lines and the case with a long
643    * line.
644    */
645   for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
646     if (i == 0)
647       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
648     else
649       SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
650                   "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
651                   "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
652                   "LONG LINE \nb\nc\nd\ne");
653     for (j = 0; j < 12; j++) /* reset scrol position to top */
654       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
655
656     /* get first visible line */
657     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
658     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
659
660     /* get new current first visible line */
661     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
662
663     ok(((r & 0xffffff00) == 0x00010000) &&
664        ((r & 0x000000ff) != 0x00000000),
665        "EM_SCROLL page down didn't scroll by a small positive number of "
666        "lines (r == 0x%08x)\n", r);
667     ok(y_after > y_before, "EM_SCROLL page down not functioning "
668        "(line %d scrolled to line %d\n", y_before, y_after);
669
670     y_before = y_after;
671     
672     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
673     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
674     ok(((r & 0xffffff00) == 0x0001ff00),
675        "EM_SCROLL page up didn't scroll by a small negative number of lines "
676        "(r == 0x%08x)\n", r);
677     ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
678        "%d scrolled to line %d\n", y_before, y_after);
679     
680     y_before = y_after;
681
682     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
683
684     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
685
686     ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
687        "(r == 0x%08x)\n", r);
688     ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
689        "1 line (%d scrolled to %d)\n", y_before, y_after);
690
691     y_before = y_after;
692
693     r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
694
695     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
696
697     ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
698        "(r == 0x%08x)\n", r);
699     ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
700        "line (%d scrolled to %d)\n", y_before, y_after);
701
702     y_before = y_after;
703
704     r = SendMessage(hwndRichEdit, EM_SCROLL,
705                     SB_LINEUP, 0); /* lineup beyond top */
706
707     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
708
709     ok(r == 0x00010000,
710        "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
711     ok(y_before == y_after,
712        "EM_SCROLL line up beyond top worked (%d)\n", y_after);
713
714     y_before = y_after;
715
716     r = SendMessage(hwndRichEdit, EM_SCROLL,
717                     SB_PAGEUP, 0);/*page up beyond top */
718
719     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
720
721     ok(r == 0x00010000,
722        "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
723     ok(y_before == y_after,
724        "EM_SCROLL page up beyond top worked (%d)\n", y_after);
725
726     for (j = 0; j < 12; j++) /* page down all the way to the bottom */
727       SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
728     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
729     r = SendMessage(hwndRichEdit, EM_SCROLL,
730                     SB_PAGEDOWN, 0); /* page down beyond bot */
731     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
732
733     ok(r == 0x00010000,
734        "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
735     ok(y_before == y_after,
736        "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
737        y_before, y_after);
738
739     y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
740     SendMessage(hwndRichEdit, EM_SCROLL,
741                 SB_LINEDOWN, 0); /* line down beyond bot */
742     y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
743     
744     ok(r == 0x00010000,
745        "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
746     ok(y_before == y_after,
747        "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
748        y_before, y_after);
749   }
750   DestroyWindow(hwndRichEdit);
751 }
752
753 START_TEST( editor )
754 {
755   MSG msg;
756   time_t end;
757
758   /* Must explicitly LoadLibrary(). The test has no references to functions in
759    * RICHED20.DLL, so the linker doesn't actually link to it. */
760   hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
761   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
762   test_EM_FINDTEXT();
763   test_EM_SCROLLCARET();
764   test_EM_SCROLL();
765   test_EM_SETTEXTMODE();
766   test_TM_PLAINTEXT();
767   test_EM_SETOPTIONS();
768   test_WM_GETTEXT();
769   test_EM_AUTOURLDETECT();
770
771   /* Set the environment variable WINETEST_RICHED20 to keep windows
772    * responsive and open for 30 seconds. This is useful for debugging.
773    *
774    * The message pump uses PeekMessage() to empty the queue and then sleeps for
775    * 50ms before retrying the queue. */
776   end = time(NULL) + 30;
777   if (getenv( "WINETEST_RICHED20" )) {
778     while (time(NULL) < end) {
779       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
780         TranslateMessage(&msg);
781         DispatchMessage(&msg);
782       } else {
783         Sleep(50);
784       }
785     }
786   }
787
788   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
789 }