~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/riched20/tests/editor.c

Version: ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2 * Unit test suite for rich edit control
  3 *
  4 * Copyright 2006 Google (Thomas Kho)
  5 * Copyright 2007 Matt Finnicum
  6 * Copyright 2007 Dmitry Timoshkov
  7 *
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License as published by the Free Software Foundation; either
 11 * version 2.1 of the License, or (at your option) any later version.
 12 *
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16 * Lesser General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU Lesser General Public
 19 * License along with this library; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 21 */
 22 
 23 #include <stdarg.h>
 24 #include <assert.h>
 25 #include <windef.h>
 26 #include <winbase.h>
 27 #include <wingdi.h>
 28 #include <winuser.h>
 29 #include <winnls.h>
 30 #include <ole2.h>
 31 #include <richedit.h>
 32 #include <time.h>
 33 #include <wine/test.h>
 34 
 35 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
 36 
 37 #define ok_w3(format, szString1, szString2, szString3) \
 38     WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
 39     WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
 40     WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
 41     ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
 42        format, string1, string2, string3);
 43 
 44 static HMODULE hmoduleRichEdit;
 45 
 46 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
 47   HWND hwnd;
 48   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
 49                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
 50                       hmoduleRichEdit, NULL);
 51   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
 52   return hwnd;
 53 }
 54 
 55 static HWND new_richedit(HWND parent) {
 56   return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
 57 }
 58 
 59 static void processPendingMessages(void)
 60 {
 61     MSG msg;
 62     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
 63         TranslateMessage(&msg);
 64         DispatchMessage(&msg);
 65     }
 66 }
 67 
 68 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
 69 {
 70     BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
 71     BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
 72     SetFocus(hwnd);
 73     keybd_event(mod_vk, mod_scan_code, 0, 0);
 74     keybd_event(vk, scan_code, 0, 0);
 75     keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
 76     keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
 77     processPendingMessages();
 78 }
 79 
 80 static void simulate_typing_characters(HWND hwnd, const char* szChars)
 81 {
 82     int ret;
 83 
 84     while (*szChars != '\0') {
 85         SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
 86         ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
 87         ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
 88         SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
 89         szChars++;
 90     }
 91 }
 92 
 93 static const char haystack[] = "WINEWine wineWine wine WineWine";
 94                              /* ^0        ^10       ^20       ^30 */
 95 
 96 struct find_s {
 97   int start;
 98   int end;
 99   const char *needle;
100   int flags;
101   int expected_loc;
102   int _todo_wine;
103 };
104 
105 
106 struct find_s find_tests[] = {
107   /* Find in empty text */
108   {0, -1, "foo", FR_DOWN, -1, 0},
109   {0, -1, "foo", 0, -1, 0},
110   {0, -1, "", FR_DOWN, -1, 0},
111   {20, 5, "foo", FR_DOWN, -1, 0},
112   {5, 20, "foo", FR_DOWN, -1, 0}
113 };
114 
115 struct find_s find_tests2[] = {
116   /* No-result find */
117   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
118   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
119 
120   /* Subsequent finds */
121   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
122   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
123   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
124   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
125 
126   /* Find backwards */
127   {19, 20, "Wine", FR_MATCHCASE, 13, 0},
128   {10, 20, "Wine", FR_MATCHCASE, 4, 0},
129   {20, 10, "Wine", FR_MATCHCASE, 13, 0},
130 
131   /* Case-insensitive */
132   {1, 31, "wInE", FR_DOWN, 4, 0},
133   {1, 31, "Wine", FR_DOWN, 4, 0},
134 
135   /* High-to-low ranges */
136   {20, 5, "Wine", FR_DOWN, -1, 0},
137   {2, 1, "Wine", FR_DOWN, -1, 0},
138   {30, 29, "Wine", FR_DOWN, -1, 0},
139   {20, 5, "Wine", 0, 13, 0},
140 
141   /* Find nothing */
142   {5, 10, "", FR_DOWN, -1, 0},
143   {10, 5, "", FR_DOWN, -1, 0},
144   {0, -1, "", FR_DOWN, -1, 0},
145   {10, 5, "", 0, -1, 0},
146 
147   /* Whole-word search */
148   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
149   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
150   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
151   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
152   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
153   {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
154   {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
155   
156   /* Bad ranges */
157   {5, 200, "XXX", FR_DOWN, -1, 0},
158   {-20, 20, "Wine", FR_DOWN, -1, 0},
159   {-20, 20, "Wine", FR_DOWN, -1, 0},
160   {-15, -20, "Wine", FR_DOWN, -1, 0},
161   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
162 
163   /* Check the case noted in bug 4479 where matches at end aren't recognized */
164   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
165   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
166   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
167   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
168   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
169 
170   /* The backwards case of bug 4479; bounds look right
171    * Fails because backward find is wrong */
172   {19, 20, "WINE", FR_MATCHCASE, 0, 0},
173   {0, 20, "WINE", FR_MATCHCASE, -1, 0},
174 
175   {0, -1, "wineWine wine", 0, -1, 0},
176 };
177 
178 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
179   int findloc;
180   FINDTEXT ft;
181   memset(&ft, 0, sizeof(ft));
182   ft.chrg.cpMin = f->start;
183   ft.chrg.cpMax = f->end;
184   ft.lpstrText = f->needle;
185   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
186   ok(findloc == f->expected_loc,
187      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
188      name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
189 }
190 
191 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
192     int id) {
193   int findloc;
194   FINDTEXTEX ft;
195   int expected_end_loc;
196 
197   memset(&ft, 0, sizeof(ft));
198   ft.chrg.cpMin = f->start;
199   ft.chrg.cpMax = f->end;
200   ft.lpstrText = f->needle;
201   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
202   ok(findloc == f->expected_loc,
203       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
204       name, id, f->needle, f->start, f->end, f->flags, findloc);
205   ok(ft.chrgText.cpMin == f->expected_loc,
206       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
207       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
208   expected_end_loc = ((f->expected_loc == -1) ? -1
209         : f->expected_loc + strlen(f->needle));
210   ok(ft.chrgText.cpMax == expected_end_loc,
211       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
212       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
213 }
214 
215 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
216     int num_tests)
217 {
218   int i;
219 
220   for (i = 0; i < num_tests; i++) {
221     if (find[i]._todo_wine) {
222       todo_wine {
223         check_EM_FINDTEXT(hwnd, name, &find[i], i);
224         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
225       }
226     } else {
227         check_EM_FINDTEXT(hwnd, name, &find[i], i);
228         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
229     }
230   }
231 }
232 
233 static void test_EM_FINDTEXT(void)
234 {
235   HWND hwndRichEdit = new_richedit(NULL);
236   CHARFORMAT2 cf2;
237 
238   /* Empty rich edit control */
239   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
240       sizeof(find_tests)/sizeof(struct find_s));
241 
242   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
243 
244   /* Haystack text */
245   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
246       sizeof(find_tests2)/sizeof(struct find_s));
247 
248   /* Setting a format on an arbitrary range should have no effect in search
249      results. This tests correct offset reporting across runs. */
250   cf2.cbSize = sizeof(CHARFORMAT2);
251   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
252              (LPARAM) &cf2);
253   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
254   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
255   SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
256   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
257 
258   /* Haystack text, again */
259   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
260       sizeof(find_tests2)/sizeof(struct find_s));
261 
262   /* Yet another range */
263   cf2.dwMask = CFM_BOLD | cf2.dwMask;
264   cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
265   SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
266   SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
267 
268   /* Haystack text, again */
269   run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
270       sizeof(find_tests2)/sizeof(struct find_s));
271 
272   DestroyWindow(hwndRichEdit);
273 }
274 
275 static const struct getline_s {
276   int line;
277   size_t buffer_len;
278   const char *text;
279 } gl[] = {
280   {0, 10, "foo bar\r"},
281   {1, 10, "\r"},
282   {2, 10, "bar\r"},
283   {3, 10, "\r"},
284 
285   /* Buffer smaller than line length */
286   {0, 2, "foo bar\r"},
287   {0, 1, "foo bar\r"},
288   {0, 0, "foo bar\r"}
289 };
290 
291 static void test_EM_GETLINE(void)
292 {
293   int i;
294   HWND hwndRichEdit = new_richedit(NULL);
295   static const int nBuf = 1024;
296   char dest[1024], origdest[1024];
297   const char text[] = "foo bar\n"
298       "\n"
299       "bar\n";
300 
301   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
302 
303   memset(origdest, 0xBB, nBuf);
304   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
305   {
306     int nCopied;
307     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
308     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
309     memset(dest, 0xBB, nBuf);
310     *(WORD *) dest = gl[i].buffer_len;
311 
312     /* EM_GETLINE appends a "\r\0" to the end of the line
313      * nCopied counts up to and including the '\r' */
314     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
315     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
316        expected_nCopied);
317     /* two special cases since a parameter is passed via dest */
318     if (gl[i].buffer_len == 0)
319       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
320          "buffer_len=0\n");
321     else if (gl[i].buffer_len == 1)
322       ok(dest[0] == gl[i].text[0] && !dest[1] &&
323          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
324     else
325     {
326       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
327          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
328       ok(!strncmp(dest + expected_bytes_written, origdest
329                   + expected_bytes_written, nBuf - expected_bytes_written),
330          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
331     }
332   }
333 
334   DestroyWindow(hwndRichEdit);
335 }
336 
337 static void test_EM_LINELENGTH(void)
338 {
339   HWND hwndRichEdit = new_richedit(NULL);
340   const char * text =
341         "richedit1\r"
342         "richedit1\n"
343         "richedit1\r\n"
344         "richedit1";
345   int offset_test[10][2] = {
346         {0, 9},
347         {5, 9},
348         {10, 9},
349         {15, 9},
350         {20, 9},
351         {25, 9},
352         {30, 9},
353         {35, 9},
354         {40, 0},
355         {45, 0},
356   };
357   int i;
358   LRESULT result;
359 
360   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
361 
362   for (i = 0; i < 10; i++) {
363     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
364     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
365         offset_test[i][0], result, offset_test[i][1]);
366   }
367 
368   DestroyWindow(hwndRichEdit);
369 }
370 
371 static int get_scroll_pos_y(HWND hwnd)
372 {
373   POINT p = {-1, -1};
374   SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
375   ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
376   return p.y;
377 }
378 
379 static void move_cursor(HWND hwnd, long charindex)
380 {
381   CHARRANGE cr;
382   cr.cpMax = charindex;
383   cr.cpMin = charindex;
384   SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
385 }
386 
387 static void line_scroll(HWND hwnd, int amount)
388 {
389   SendMessage(hwnd, EM_LINESCROLL, 0, amount);
390 }
391 
392 static void test_EM_SCROLLCARET(void)
393 {
394   int prevY, curY;
395   HWND hwndRichEdit = new_richedit(NULL);
396   const char text[] = "aa\n"
397       "this is a long line of text that should be longer than the "
398       "control's width\n"
399       "cc\n"
400       "dd\n"
401       "ee\n"
402       "ff\n"
403       "gg\n"
404       "hh\n";
405 
406   /* Can't verify this */
407   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
408 
409   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
410 
411   /* Caret above visible window */
412   line_scroll(hwndRichEdit, 3);
413   prevY = get_scroll_pos_y(hwndRichEdit);
414   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
415   curY = get_scroll_pos_y(hwndRichEdit);
416   ok(prevY != curY, "%d == %d\n", prevY, curY);
417 
418   /* Caret below visible window */
419   move_cursor(hwndRichEdit, sizeof(text) - 1);
420   line_scroll(hwndRichEdit, -3);
421   prevY = get_scroll_pos_y(hwndRichEdit);
422   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
423   curY = get_scroll_pos_y(hwndRichEdit);
424   ok(prevY != curY, "%d == %d\n", prevY, curY);
425 
426   /* Caret in visible window */
427   move_cursor(hwndRichEdit, sizeof(text) - 2);
428   prevY = get_scroll_pos_y(hwndRichEdit);
429   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
430   curY = get_scroll_pos_y(hwndRichEdit);
431   ok(prevY == curY, "%d != %d\n", prevY, curY);
432 
433   /* Caret still in visible window */
434   line_scroll(hwndRichEdit, -1);
435   prevY = get_scroll_pos_y(hwndRichEdit);
436   SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
437   curY = get_scroll_pos_y(hwndRichEdit);
438   ok(prevY == curY, "%d != %d\n", prevY, curY);
439 
440   DestroyWindow(hwndRichEdit);
441 }
442 
443 static void test_EM_POSFROMCHAR(void)
444 {
445   HWND hwndRichEdit = new_richedit(NULL);
446   int i;
447   LRESULT result;
448   unsigned int height = 0;
449   int xpos = 0;
450   static const char text[] = "aa\n"
451       "this is a long line of text that should be longer than the "
452       "control's width\n"
453       "cc\n"
454       "dd\n"
455       "ee\n"
456       "ff\n"
457       "gg\n"
458       "hh\n";
459 
460   /* Fill the control to lines to ensure that most of them are offscreen */
461   for (i = 0; i < 50; i++)
462   {
463     /* Do not modify the string; it is exactly 16 characters long. */
464     SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
465     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
466   }
467 
468   /*
469    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
470    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
471    Richedit 3.0 accepts either of the above API conventions.
472    */
473 
474   /* Testing Richedit 2.0 API format */
475 
476   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
477      Since all lines are identical and drawn with the same font,
478      they should have the same height... right?
479    */
480   for (i = 0; i < 50; i++)
481   {
482     /* All the lines are 16 characters long */
483     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
484     if (i == 0)
485     {
486       ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
487       todo_wine {
488       ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
489       }
490       xpos = LOWORD(result);
491     }
492     else if (i == 1)
493     {
494       ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
495       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
496       height = HIWORD(result);
497     }
498     else
499     {
500       ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
501       ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
502     }
503   }
504 
505   /* Testing position at end of text */
506   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
507   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
508   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
509 
510   /* Testing position way past end of text */
511   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
512   ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
513   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
514 
515   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
516   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
517   for (i = 0; i < 50; i++)
518   {
519     /* All the lines are 16 characters long */
520     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
521     ok((signed short)(HIWORD(result)) == (i - 1) * height,
522         "EM_POSFROMCHAR reports y=%hd, expected %d\n",
523         (signed short)(HIWORD(result)), (i - 1) * height);
524     ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
525   }
526 
527   /* Testing position at end of text */
528   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
529   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
530   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
531 
532   /* Testing position way past end of text */
533   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
534   ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
535   ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
536 
537   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
538   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
539   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
540 
541   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
542   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
543   todo_wine {
544   ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
545   }
546   xpos = LOWORD(result);
547 
548   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
549   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
550   ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
551   todo_wine {
552   /* Fails on builtin because horizontal scrollbar is not being shown */
553   ok((signed short)(LOWORD(result)) < xpos,
554         "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
555         (signed short)(LOWORD(result)), xpos);
556   }
557   DestroyWindow(hwndRichEdit);
558 }
559 
560 static void test_EM_SETCHARFORMAT(void)
561 {
562   HWND hwndRichEdit = new_richedit(NULL);
563   CHARFORMAT2 cf2;
564   int rc = 0;
565   int tested_effects[] = {
566     CFE_BOLD,
567     CFE_ITALIC,
568     CFE_UNDERLINE,
569     CFE_STRIKEOUT,
570     CFE_PROTECTED,
571     CFE_LINK,
572     CFE_SUBSCRIPT,
573     CFE_SUPERSCRIPT,
574     0
575   };
576   int i;
577   CHARRANGE cr;
578 
579   /* Invalid flags, CHARFORMAT2 structure blanked out */
580   memset(&cf2, 0, sizeof(cf2));
581   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
582              (LPARAM) &cf2);
583   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
584 
585   /* A valid flag, CHARFORMAT2 structure blanked out */
586   memset(&cf2, 0, sizeof(cf2));
587   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
588              (LPARAM) &cf2);
589   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
590 
591   /* A valid flag, CHARFORMAT2 structure blanked out */
592   memset(&cf2, 0, sizeof(cf2));
593   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
594              (LPARAM) &cf2);
595   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
596 
597   /* A valid flag, CHARFORMAT2 structure blanked out */
598   memset(&cf2, 0, sizeof(cf2));
599   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
600              (LPARAM) &cf2);
601   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
602 
603   /* A valid flag, CHARFORMAT2 structure blanked out */
604   memset(&cf2, 0, sizeof(cf2));
605   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
606              (LPARAM) &cf2);
607   ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
608 
609   /* Invalid flags, CHARFORMAT2 structure minimally filled */
610   memset(&cf2, 0, sizeof(cf2));
611   cf2.cbSize = sizeof(CHARFORMAT2);
612   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
613              (LPARAM) &cf2);
614   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
615   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
616   ok(rc == FALSE, "Should not be able to undo here.\n");
617   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
618 
619   /* A valid flag, CHARFORMAT2 structure minimally filled */
620   memset(&cf2, 0, sizeof(cf2));
621   cf2.cbSize = sizeof(CHARFORMAT2);
622   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
623              (LPARAM) &cf2);
624   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
625   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
626   ok(rc == FALSE, "Should not be able to undo here.\n");
627   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
628 
629   /* A valid flag, CHARFORMAT2 structure minimally filled */
630   memset(&cf2, 0, sizeof(cf2));
631   cf2.cbSize = sizeof(CHARFORMAT2);
632   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
633              (LPARAM) &cf2);
634   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
635   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
636   ok(rc == FALSE, "Should not be able to undo here.\n");
637   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
638 
639   /* A valid flag, CHARFORMAT2 structure minimally filled */
640   memset(&cf2, 0, sizeof(cf2));
641   cf2.cbSize = sizeof(CHARFORMAT2);
642   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
643              (LPARAM) &cf2);
644   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
645   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
646   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
647   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
648 
649   /* A valid flag, CHARFORMAT2 structure minimally filled */
650   memset(&cf2, 0, sizeof(cf2));
651   cf2.cbSize = sizeof(CHARFORMAT2);
652   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
653              (LPARAM) &cf2);
654   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
655   rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
656   todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
657   SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
658 
659   cf2.cbSize = sizeof(CHARFORMAT2);
660   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
661              (LPARAM) &cf2);
662 
663   /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
664   cf2.cbSize = sizeof(CHARFORMAT2);
665   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
666              (LPARAM) &cf2);
667   cf2.dwMask = CFM_ITALIC | cf2.dwMask;
668   cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
669 
670   /* wParam==0 is default char format, does not set modify */
671   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
672   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
673   ok(rc == 0, "Text marked as modified, expected not modified!\n");
674   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
675   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
676   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
677   ok(rc == 0, "Text marked as modified, expected not modified!\n");
678 
679   /* wParam==SCF_SELECTION sets modify if nonempty selection */
680   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
681   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
682   ok(rc == 0, "Text marked as modified, expected not modified!\n");
683   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
684   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
685   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
686   ok(rc == 0, "Text marked as modified, expected not modified!\n");
687 
688   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
689   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
690   ok(rc == 0, "Text marked as modified, expected not modified!\n");
691   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
692   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
693   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
694   ok(rc == 0, "Text marked as modified, expected not modified!\n");
695   SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
696   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
697   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
698   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
699   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
700 
701   /* wParam==SCF_ALL sets modify regardless of whether text is present */
702   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
703   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
704   ok(rc == 0, "Text marked as modified, expected not modified!\n");
705   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
706   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
707   rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
708   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
709 
710   DestroyWindow(hwndRichEdit);
711 
712   /* EM_GETCHARFORMAT tests */
713   for (i = 0; tested_effects[i]; i++)
714   {
715     hwndRichEdit = new_richedit(NULL);
716     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
717 
718     /* Need to set a TrueType font to get consistent CFM_BOLD results */
719     memset(&cf2, 0, sizeof(CHARFORMAT2));
720     cf2.cbSize = sizeof(CHARFORMAT2);
721     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
722     cf2.dwEffects = 0;
723     strcpy(cf2.szFaceName, "Courier New");
724     cf2.wWeight = FW_DONTCARE;
725     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
726 
727     memset(&cf2, 0, sizeof(CHARFORMAT2));
728     cf2.cbSize = sizeof(CHARFORMAT2);
729     SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
730     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
731     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
732           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
733           ||
734           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
735         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
736     ok((cf2.dwEffects & tested_effects[i]) == 0,
737         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
738 
739     memset(&cf2, 0, sizeof(CHARFORMAT2));
740     cf2.cbSize = sizeof(CHARFORMAT2);
741     cf2.dwMask = tested_effects[i];
742     if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
743       cf2.dwMask = CFM_SUPERSCRIPT;
744     cf2.dwEffects = tested_effects[i];
745     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
746     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
747 
748     memset(&cf2, 0, sizeof(CHARFORMAT2));
749     cf2.cbSize = sizeof(CHARFORMAT2);
750     SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
751     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
752     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
753           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
754           ||
755           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
756         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
757     ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
758         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
759 
760     memset(&cf2, 0, sizeof(CHARFORMAT2));
761     cf2.cbSize = sizeof(CHARFORMAT2);
762     SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
763     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
764     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
765           (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
766           ||
767           (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
768         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
769     ok((cf2.dwEffects & tested_effects[i]) == 0,
770         "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
771 
772     memset(&cf2, 0, sizeof(CHARFORMAT2));
773     cf2.cbSize = sizeof(CHARFORMAT2);
774     SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
775     SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
776     ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
777           (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
778           ||
779           (cf2.dwMask & tested_effects[i]) == 0),
780         "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
781 
782     DestroyWindow(hwndRichEdit);
783   }
784 
785   for (i = 0; tested_effects[i]; i++)
786   {
787     hwndRichEdit = new_richedit(NULL);
788     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
789 
790     /* Need to set a TrueType font to get consistent CFM_BOLD results */
791     memset(&cf2, 0, sizeof(CHARFORMAT2));
792     cf2.cbSize = sizeof(CHARFORMAT2);
793     cf2.dwMask = CFM_FACE|CFM_WEIGHT;
794     cf2.dwEffects = 0;
795     strcpy(cf2.szFaceName, "Courier New");
796     cf2.wWeight = FW_DONTCARE;
797     SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
798 
799     memset(&cf2, 0, sizeof(CHARFORMAT2));
800     cf2.cbSize = sizeof(CHARFORMAT2);
801     cf2.dwMask = tested_effects[i];