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