Author: cwittich Date: Mon Dec 29 02:27:52 2008 New Revision: 38441
URL: http://svn.reactos.org/svn/reactos?rev=38441&view=rev Log: sync riched20 winetest to wine 1.1.11
Added: trunk/rostests/winetests/riched20/txtsrv.c (with props) Modified: trunk/rostests/winetests/riched20/editor.c trunk/rostests/winetests/riched20/riched20.rbuild trunk/rostests/winetests/riched20/testlist.c
Modified: trunk/rostests/winetests/riched20/editor.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/riched20/editor.... ============================================================================== --- trunk/rostests/winetests/riched20/editor.c [iso-8859-1] (original) +++ trunk/rostests/winetests/riched20/editor.c [iso-8859-1] Mon Dec 29 02:27:52 2008 @@ -21,6 +21,7 @@ */
#include <stdarg.h> +#include <stdio.h> #include <assert.h> #include <windef.h> #include <winbase.h> @@ -32,7 +33,18 @@ #include <time.h> #include <wine/test.h>
+static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH]; + +#define ok_w3(format, szString1, szString2, szString3) \ + WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \ + WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \ + WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \ + ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \ + format, string1, string2, string3); + static HMODULE hmoduleRichEdit; + +static int is_win9x = 0;
static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) { HWND hwnd; @@ -45,6 +57,26 @@
static HWND new_richedit(HWND parent) { return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent); +} + +/* Keeps the window reponsive for the deley_time in seconds. + * This is useful for debugging a test to see what is happening. */ +static void keep_responsive(time_t delay_time) +{ + MSG msg; + time_t end; + + /* The message pump uses PeekMessage() to empty the queue and then + * sleeps for 50ms before retrying the queue. */ + end = time(NULL) + delay_time; + while (time(NULL) < end) { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + Sleep(50); + } + } }
static void processPendingMessages(void) @@ -81,6 +113,34 @@ } }
+static BOOL hold_key(int vk) +{ + BYTE key_state[256]; + BOOL result; + + result = GetKeyboardState(key_state); + ok(result, "GetKeyboardState failed.\n"); + if (!result) return FALSE; + key_state[vk] |= 0x80; + result = SetKeyboardState(key_state); + ok(result, "SetKeyboardState failed.\n"); + return result != 0; +} + +static BOOL release_key(int vk) +{ + BYTE key_state[256]; + BOOL result; + + result = GetKeyboardState(key_state); + ok(result, "GetKeyboardState failed.\n"); + if (!result) return FALSE; + key_state[vk] &= ~0x80; + result = SetKeyboardState(key_state); + ok(result, "SetKeyboardState failed.\n"); + return result != 0; +} + static const char haystack[] = "WINEWine wineWine wine WineWine"; /* ^0 ^10 ^20 ^30 */
@@ -90,80 +150,79 @@ const char *needle; int flags; int expected_loc; - int _todo_wine; };
struct find_s find_tests[] = { /* Find in empty text */ - {0, -1, "foo", FR_DOWN, -1, 0}, - {0, -1, "foo", 0, -1, 0}, - {0, -1, "", FR_DOWN, -1, 0}, - {20, 5, "foo", FR_DOWN, -1, 0}, - {5, 20, "foo", FR_DOWN, -1, 0} + {0, -1, "foo", FR_DOWN, -1}, + {0, -1, "foo", 0, -1}, + {0, -1, "", FR_DOWN, -1}, + {20, 5, "foo", FR_DOWN, -1}, + {5, 20, "foo", FR_DOWN, -1} };
struct find_s find_tests2[] = { /* No-result find */ - {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0}, - {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0}, + {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1}, + {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
/* Subsequent finds */ - {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0}, - {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0}, - {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0}, - {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0}, + {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4}, + {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13}, + {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23}, + {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
/* Find backwards */ - {19, 20, "Wine", FR_MATCHCASE, 13, 0}, - {10, 20, "Wine", FR_MATCHCASE, 4, 0}, - {20, 10, "Wine", FR_MATCHCASE, 13, 0}, + {19, 20, "Wine", FR_MATCHCASE, 13}, + {10, 20, "Wine", FR_MATCHCASE, 4}, + {20, 10, "Wine", FR_MATCHCASE, 13},
/* Case-insensitive */ - {1, 31, "wInE", FR_DOWN, 4, 0}, - {1, 31, "Wine", FR_DOWN, 4, 0}, + {1, 31, "wInE", FR_DOWN, 4}, + {1, 31, "Wine", FR_DOWN, 4},
/* High-to-low ranges */ - {20, 5, "Wine", FR_DOWN, -1, 0}, - {2, 1, "Wine", FR_DOWN, -1, 0}, - {30, 29, "Wine", FR_DOWN, -1, 0}, - {20, 5, "Wine", 0, 13, 0}, + {20, 5, "Wine", FR_DOWN, -1}, + {2, 1, "Wine", FR_DOWN, -1}, + {30, 29, "Wine", FR_DOWN, -1}, + {20, 5, "Wine", 0, 13},
/* Find nothing */ - {5, 10, "", FR_DOWN, -1, 0}, - {10, 5, "", FR_DOWN, -1, 0}, - {0, -1, "", FR_DOWN, -1, 0}, - {10, 5, "", 0, -1, 0}, + {5, 10, "", FR_DOWN, -1}, + {10, 5, "", FR_DOWN, -1}, + {0, -1, "", FR_DOWN, -1}, + {10, 5, "", 0, -1},
/* Whole-word search */ - {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0}, - {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0}, - {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0}, - {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0}, - {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0}, - {11, -1, "winewine", FR_WHOLEWORD, 0, 0}, - {31, -1, "winewine", FR_WHOLEWORD, 23, 0}, + {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18}, + {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1}, + {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18}, + {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0}, + {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23}, + {11, -1, "winewine", FR_WHOLEWORD, 0}, + {31, -1, "winewine", FR_WHOLEWORD, 23},
/* Bad ranges */ - {5, 200, "XXX", FR_DOWN, -1, 0}, - {-20, 20, "Wine", FR_DOWN, -1, 0}, - {-20, 20, "Wine", FR_DOWN, -1, 0}, - {-15, -20, "Wine", FR_DOWN, -1, 0}, - {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0}, + {5, 200, "XXX", FR_DOWN, -1}, + {-20, 20, "Wine", FR_DOWN, -1}, + {-20, 20, "Wine", FR_DOWN, -1}, + {-15, -20, "Wine", FR_DOWN, -1}, + {1<<12, 1<<13, "Wine", FR_DOWN, -1},
/* Check the case noted in bug 4479 where matches at end aren't recognized */ - {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0}, - {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0}, - {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0}, - {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0}, - {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0}, + {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23}, + {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27}, + {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27}, + {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23}, + {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
/* The backwards case of bug 4479; bounds look right * Fails because backward find is wrong */ - {19, 20, "WINE", FR_MATCHCASE, 0, 0}, - {0, 20, "WINE", FR_MATCHCASE, -1, 0}, - - {0, -1, "wineWine wine", 0, -1, 0}, + {19, 20, "WINE", FR_MATCHCASE, 0}, + {0, 20, "WINE", FR_MATCHCASE, -1}, + + {0, -1, "wineWine wine", 0, -1}, };
static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) { @@ -209,15 +268,8 @@ int i;
for (i = 0; i < num_tests; i++) { - if (find[i]._todo_wine) { - todo_wine { - check_EM_FINDTEXT(hwnd, name, &find[i], i); - check_EM_FINDTEXTEX(hwnd, name, &find[i], i); - } - } else { - check_EM_FINDTEXT(hwnd, name, &find[i], i); - check_EM_FINDTEXTEX(hwnd, name, &find[i], i); - } + check_EM_FINDTEXT(hwnd, name, &find[i], i); + check_EM_FINDTEXTEX(hwnd, name, &find[i], i); } }
@@ -296,7 +348,7 @@ { int nCopied; int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text)); - int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1); + int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text)); memset(dest, 0xBB, nBuf); *(WORD *) dest = gl[i].buffer_len;
@@ -314,11 +366,37 @@ !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n"); else { + /* Prepare hex strings of buffers to dump on failure. */ + char expectedbuf[1024]; + char resultbuf[1024]; + int j; + resultbuf[0] = '\0'; + for (j = 0; j < 32; j++) + sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF); + expectedbuf[0] = '\0'; + for (j = 0; j < expected_bytes_written; j++) /* Written bytes */ + sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF); + for (; j < gl[i].buffer_len; j++) /* Ignored bytes */ + sprintf(expectedbuf+strlen(expectedbuf), "??"); + for (; j < 32; j++) /* Bytes after declared buffer size */ + sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF); + + /* Test the part of the buffer that is expected to be written according + * to the MSDN documentation fo EM_GETLINE, which does not state that + * a NULL terminating character will be added unless no text is copied. + * + * Windows 95, 98 & NT do not append a NULL terminating character, but + * Windows 2000 and up do append a NULL terminating character if there + * is space in the buffer. The test will ignore this difference. */ ok(!strncmp(dest, gl[i].text, expected_bytes_written), - "%d: expected_bytes_written=%d\n", i, expected_bytes_written); - ok(!strncmp(dest + expected_bytes_written, origdest - + expected_bytes_written, nBuf - expected_bytes_written), - "%d: expected_bytes_written=%d\n", i, expected_bytes_written); + "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n", + i, expected_bytes_written, expectedbuf, resultbuf); + /* Test the part of the buffer after the declared length to make sure + * there are no buffer overruns. */ + ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len, + nBuf - gl[i].buffer_len), + "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n", + i, expected_bytes_written, expectedbuf, resultbuf); } }
@@ -383,7 +461,6 @@ static void test_EM_SCROLLCARET(void) { int prevY, curY; - HWND hwndRichEdit = new_richedit(NULL); const char text[] = "aa\n" "this is a long line of text that should be longer than the " "control's width\n" @@ -393,6 +470,14 @@ "ff\n" "gg\n" "hh\n"; + /* The richedit window height needs to be large enough vertically to fit in + * more than two lines of text, so the new_richedit function can't be used + * since a height of 60 was not large enough on some systems. + */ + HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, + ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE, + 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
/* Can't verify this */ SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); @@ -438,6 +523,7 @@ LRESULT result; unsigned int height = 0; int xpos = 0; + POINTL pt; static const char text[] = "aa\n" "this is a long line of text that should be longer than the " "control's width\n" @@ -475,9 +561,7 @@ if (i == 0) { ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result)); - todo_wine { ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result)); - } xpos = LOWORD(result); } else if (i == 1) @@ -531,9 +615,7 @@
result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0); ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result)); - todo_wine { ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result)); - } xpos = LOWORD(result);
SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0); @@ -545,6 +627,26 @@ "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n", (signed short)(LOWORD(result)), xpos); } + SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0); + + /* Test around end of text that doesn't end in a newline. */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234"); + SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, + SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1); + ok(pt.x > 1, "pt.x = %d\n", pt.x); + xpos = pt.x; + SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, + SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)); + ok(pt.x > xpos, "pt.x = %d\n", pt.x); + xpos = pt.x; + SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, + SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1); + ok(pt.x == xpos, "pt.x = %d\n", pt.x); + + /* Try a negative position. */ + SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1); + ok(pt.x == 1, "pt.x = %d\n", pt.x); + DestroyWindow(hwndRichEdit); }
@@ -659,7 +761,7 @@ cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
/* wParam==0 is default char format, does not set modify */ - SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); ok(rc == 0, "Text marked as modified, expected not modified!\n"); rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2); @@ -668,7 +770,7 @@ ok(rc == 0, "Text marked as modified, expected not modified!\n");
/* wParam==SCF_SELECTION sets modify if nonempty selection */ - SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); ok(rc == 0, "Text marked as modified, expected not modified!\n"); rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2); @@ -690,7 +792,7 @@ ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
/* wParam==SCF_ALL sets modify regardless of whether text is present */ - SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); ok(rc == 0, "Text marked as modified, expected not modified!\n"); rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2); @@ -1092,6 +1194,7 @@ HWND hwndRichEdit = new_richedit(NULL); PARAFORMAT2 fmt; HRESULT ret; + LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER; fmt.cbSize = sizeof(PARAFORMAT2); fmt.dwMask = PFM_ALIGNMENT; fmt.wAlignment = PFA_LEFT; @@ -1102,8 +1205,14 @@ fmt.cbSize = sizeof(PARAFORMAT2); fmt.dwMask = -1; ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt); - ok(ret == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, ret); - ok(fmt.dwMask == PFM_ALL2, "expected %x got %x\n", PFM_ALL2, fmt.dwMask); + /* Ignore the PFM_TABLEROWDELIMITER bit because it changes + * between richedit different native builds of riched20.dll + * used on different Windows versions. */ + ret &= ~PFM_TABLEROWDELIMITER; + fmt.dwMask &= ~PFM_TABLEROWDELIMITER; + + ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret); + ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
DestroyWindow(hwndRichEdit); } @@ -2253,7 +2362,7 @@ unsigned int recursionLevel = 0; unsigned int WM_SIZE_recursionLevel = 0; BOOL bailedOutOfRecursion = FALSE; -LRESULT WINAPI (*richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -2307,8 +2416,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); @@ -2318,8 +2427,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text); @@ -2584,8 +2693,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); @@ -2595,8 +2704,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a"); @@ -2606,8 +2715,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); @@ -2617,8 +2726,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text); @@ -2732,8 +2841,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0), "Vertical scrollbar is invisible, should be visible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
/* Ditto, see above */ @@ -2744,8 +2853,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0), "Vertical scrollbar is invisible, should be visible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
/* Ditto, see above */ @@ -2756,8 +2865,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0), "Vertical scrollbar is invisible, should be visible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
/* Ditto, see above */ @@ -2768,8 +2877,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0), "Vertical scrollbar is invisible, should be visible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
/* Ditto, see above */ @@ -2780,8 +2889,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0), "Vertical scrollbar is invisible, should be visible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text); @@ -2807,8 +2916,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); @@ -2818,8 +2927,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a"); @@ -2829,8 +2938,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); @@ -2840,8 +2949,8 @@ GetScrollInfo(hwndRichEdit, SB_VERT, &si); ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0), "Vertical scrollbar is visible, should be invisible.\n"); - ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 0, - "reported page/range is %d (%d..%d) expected all 0\n", + ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100), + "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n", si.nPage, si.nMin, si.nMax);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text); @@ -3207,6 +3316,8 @@ 't', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't', 0}; + WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t', + '\r','t','S','o','m','e','T','e','x','t',0}; WCHAR TestItem2[] = {'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't', @@ -3341,8 +3452,7 @@ "EM_SETTEXTEX did not convert properly\n");
/* !ST_SELECTION && Unicode && !\rtf */ - result = SendMessage(hwndRichEdit, EM_SETTEXTEX, - (WPARAM)&setText, (LPARAM) NULL); + result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0); SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
ok (result == 1, @@ -3359,8 +3469,7 @@ SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); /* replace current selection: ST_SELECTION && Unicode && !\rtf */ setText.flags = ST_SELECTION; - result = SendMessage(hwndRichEdit, EM_SETTEXTEX, - (WPARAM)&setText, (LPARAM) NULL); + result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0); ok(result == 0, "EM_SETTEXTEX with NULL lParam to replace selection" " with no text should return 0. Got %i\n", @@ -3439,9 +3548,7 @@ setText.flags = ST_SELECTION; SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf); SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); - ok(lstrcmpW(buf, TestItem1alt) == 0, - "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when" - " using ST_SELECTION on an RTF string and non-Unicode\n"); + ok_w3("Expected "%s" or "%s", got "%s"\n", TestItem1alt, TestItem1altn, buf);
/* The following test demonstrates that EM_SETTEXTEX replacing a selection */ setText.codepage = 1200; /* no constant for unicode */ @@ -3472,6 +3579,28 @@ "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when" " using ST_SELECTION and non-Unicode\n");
+ /* Test setting text using rich text format */ + setText.flags = 0; + setText.codepage = CP_ACP; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\rtf richtext}"); + getText.codepage = CP_ACP; + getText.cb = MAX_BUF_LEN; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP); + ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP); + + setText.flags = 0; + setText.codepage = CP_ACP; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\urtf morerichtext}"); + getText.codepage = CP_ACP; + getText.cb = MAX_BUF_LEN; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP); + ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
DestroyWindow(hwndRichEdit); } @@ -3686,7 +3815,7 @@ returnedCF2A.cbSize = sizeof(returnedCF2A);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x"); - SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0)); SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont); @@ -3694,14 +3823,14 @@ "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n", sentLogFont.lfFaceName,returnedCF2A.szFaceName);
- SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0)); SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A); GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont); ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName), "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n", sentLogFont.lfFaceName,returnedCF2A.szFaceName);
- SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0)); SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A); GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont); ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName), @@ -3714,7 +3843,7 @@ ZeroMemory(&sentLogFont,sizeof(sentLogFont)); returnedCF2A.cbSize = sizeof(returnedCF2A);
- SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0)); SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A); GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont); ok (!strcmp("System",returnedCF2A.szFaceName), @@ -3788,7 +3917,7 @@
/* setting font doesn't change modify flag */ SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); - SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0)); result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); ok (result == 0, "EM_GETMODIFY returned non-zero, instead of zero on setting font\n"); @@ -4007,7 +4136,7 @@
/* FIXME add more tests */ SendMessage(hwndRichEdit, EM_SETSEL, 7, 17); - r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0); ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r); SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); r = strcmp(buffer, "testing"); @@ -4021,7 +4150,7 @@ SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
/* Test behavior with carriage returns and newlines */ - SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1"); ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r); SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); @@ -4040,7 +4169,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r"); ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r); SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); @@ -4066,7 +4195,7 @@ characters interpreted from the original lParam. Wine's builtin riched20 implements the WinXP behavior. */ - SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n"); ok(11 == r /* WinXP */ || 10 == r /* Win98 */, "EM_REPLACESEL returned %d, expected 11 or 10\n", r); @@ -4103,7 +4232,7 @@ string. */
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r"); ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r); r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); @@ -4125,7 +4254,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n"); ok(3 == r /* WinXP */ || 1 == r /* Win98 */, "EM_REPLACESEL returned %d, expected 3 or 1\n", r); @@ -4148,7 +4277,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r"); ok(9 == r /* WinXP */ || 7 == r /* Win98 */, "EM_REPLACESEL returned %d, expected 9 or 7\n", r); @@ -4171,7 +4300,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n"); ok(5 == r /* WinXP */ || 2 == r /* Win98 */, "EM_REPLACESEL returned %d, expected 5 or 2\n", r); @@ -4194,7 +4323,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r"); ok(5 == r /* WinXP */ || 3 == r /* Win98 */, "EM_REPLACESEL returned %d, expected 5 or 3\n", r); @@ -4217,7 +4346,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r"); ok(6 == r /* WinXP */ || 5 == r /* Win98 */, "EM_REPLACESEL returned %d, expected 6 or 5\n", r); @@ -4240,7 +4369,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n"); ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r); r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); @@ -4262,7 +4391,7 @@ r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
- SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n"); ok(9 == r /* WinXP */ || 7 == r /* Win98 */, "EM_REPLACESEL returned %d, expected 9 or 7\n", r); @@ -4305,11 +4434,15 @@ const char* text3 = "testing paste\r\npaste\r\ntesting paste"; HWND hwndRichEdit = new_richedit(NULL);
- /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP - messages, probably because it inspects the keyboard state itself. - Therefore, native requires this in order to obey Ctrl-<key> keystrokes. + /* Native riched20 inspects the keyboard state (e.g. GetKeyState) + * to test the state of the modifiers (Ctrl/Alt/Shift). + * + * Therefore Ctrl-<key> keystrokes need to be simulated with + * keybd_event or by using SetKeyboardState to set the modifiers + * and SendMessage to simulate the keystrokes. */
+ /* Sent keystrokes with keybd_event */ #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C') #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X') #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V') @@ -4326,13 +4459,14 @@ /* Pasted text should be visible at this step */ result = strcmp(text1_step1, buffer); ok(result == 0, - "test paste: strcmp = %i\n", result); + "test paste: strcmp = %i, text='%s'\n", result, buffer); + SEND_CTRL_Z(hwndRichEdit); /* Undo */ SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); /* Text should be the same as before (except for \r -> \r\n conversion) */ result = strcmp(text1_after, buffer); ok(result == 0, - "test paste: strcmp = %i\n", result); + "test paste: strcmp = %i, text='%s'\n", result, buffer);
SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2); SendMessage(hwndRichEdit, EM_SETSEL, 8, 13); @@ -4357,6 +4491,87 @@ ok(result == 0, "test paste: strcmp = %i\n", result);
+#undef SEND_CTRL_C +#undef SEND_CTRL_X +#undef SEND_CTRL_V +#undef SEND_CTRL_Z +#undef SEND_CTRL_Y + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); + /* Send WM_CHAR to simulates Ctrl-V */ + SendMessage(hwndRichEdit, WM_CHAR, 22, + (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + /* Shouldn't paste because pasting is handled by WM_KEYDOWN */ + result = strcmp(buffer,""); + ok(result == 0, + "test paste: strcmp = %i, actual = '%s'\n", result, buffer); + + /* Send keystrokes with WM_KEYDOWN after setting the modifiers + * with SetKeyboard state. */ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); + /* Simulates paste (Ctrl-V) */ + hold_key(VK_CONTROL); + SendMessage(hwndRichEdit, WM_KEYDOWN, 'V', + (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1); + release_key(VK_CONTROL); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer,"paste"); + ok(result == 0, + "test paste: strcmp = %i, actual = '%s'\n", result, buffer); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1); + SendMessage(hwndRichEdit, EM_SETSEL, 0, 7); + /* Simulates copy (Ctrl-C) */ + hold_key(VK_CONTROL); + SendMessage(hwndRichEdit, WM_KEYDOWN, 'C', + (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1); + release_key(VK_CONTROL); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer,"testing"); + ok(result == 0, + "test paste: strcmp = %i, actual = '%s'\n", result, buffer); + + /* Cut with WM_KEYDOWN to simulate Ctrl-X */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut"); + /* Simulates select all (Ctrl-A) */ + hold_key(VK_CONTROL); + SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', + (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1); + /* Simulates select cut (Ctrl-X) */ + SendMessage(hwndRichEdit, WM_KEYDOWN, 'X', + (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1); + release_key(VK_CONTROL); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer,""); + ok(result == 0, + "test paste: strcmp = %i, actual = '%s'\n", result, buffer); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0); + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer,"cut\r\n"); + todo_wine ok(result == 0, + "test paste: strcmp = %i, actual = '%s'\n", result, buffer); + /* Simulates undo (Ctrl-Z) */ + hold_key(VK_CONTROL); + SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z', + (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer,""); + ok(result == 0, + "test paste: strcmp = %i, actual = '%s'\n", result, buffer); + /* Simulates redo (Ctrl-Y) */ + SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y', + (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer,"cut\r\n"); + todo_wine ok(result == 0, + "test paste: strcmp = %i, actual = '%s'\n", result, buffer); + release_key(VK_CONTROL); + DestroyWindow(hwndRichEdit); }
@@ -4379,14 +4594,14 @@ fr.chrg.cpMin = 0; fr.chrg.cpMax = 20;
- r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL); + r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0); todo_wine { ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r); }
r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr); todo_wine { - ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r); + ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r); }
fr.chrg.cpMin = 0; @@ -4397,7 +4612,7 @@ ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r); }
- r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL); + r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0); todo_wine { ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r); } @@ -4486,26 +4701,26 @@ const char * streamText0b = "{\rtf1 TestSomeText\par\par}";
const char * streamText1 = - "{\rtf1\ansi\ansicpg1252\deff0\deflang12298{\fonttbl{\f0\fswiss\fprq2\fcharset0 System;}}\r\n" \ - "\viewkind4\uc1\pard\f0\fs17 TestSomeText\par\r\n" \ + "{\rtf1\ansi\ansicpg1252\deff0\deflang12298{\fonttbl{\f0\fswiss\fprq2\fcharset0 System;}}\r\n" + "\viewkind4\uc1\pard\f0\fs17 TestSomeText\par\r\n" "}\r\n";
/* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */ const char * streamText2 = - "{{\colortbl;\red0\green255\blue102;\red255\green255\blue255;" \ - "\red170\green255\blue255;\red255\green238\blue0;\red51\green255" \ - "\blue221;\red238\green238\blue238;}\tx0 \tx424 \tx848 \tx1272 " \ - "\tx1696 \tx2120 \tx2544 \tx2968 \tx3392 \tx3816 \tx4240 \tx4664 " \ - "\tx5088 \tx5512 \tx5936 \tx6360 \tx6784 \tx7208 \tx7632 \tx8056 " \ - "\tx8480 \tx8904 \tx9328 \tx9752 \tx10176 \tx10600 \tx11024 " \ + "{{\colortbl;\red0\green255\blue102;\red255\green255\blue255;" + "\red170\green255\blue255;\red255\green238\blue0;\red51\green255" + "\blue221;\red238\green238\blue238;}\tx0 \tx424 \tx848 \tx1272 " + "\tx1696 \tx2120 \tx2544 \tx2968 \tx3392 \tx3816 \tx4240 \tx4664 " + "\tx5088 \tx5512 \tx5936 \tx6360 \tx6784 \tx7208 \tx7632 \tx8056 " + "\tx8480 \tx8904 \tx9328 \tx9752 \tx10176 \tx10600 \tx11024 " "\tx11448 \tx11872 \tx12296 \tx12720 \tx13144 \cf2 RichEdit1\line }";
const char * streamText3 = "RichEdit1";
struct StringWithLength cookieForStream4; const char * streamText4 = - "This text just needs to be long enough to cause run to be split onto "\ - "two separate lines and make sure the null terminating character is "\ + "This text just needs to be long enough to cause run to be split onto " + "two separate lines and make sure the null terminating character is " "handled properly.\0"; int length4 = strlen(streamText4) + 1; cookieForStream4.buffer = (char *)streamText4; @@ -4698,9 +4913,7 @@ char bufA[64]; WCHAR bufW[64]; HWND hwnd; - int is_win9x, em_settextex_supported, ret; - - is_win9x = GetVersion() & 0x80000000; + int em_settextex_supported, ret;
#define set_textA(hwnd, wm_set_text, txt) \ do { \ @@ -4933,8 +5146,12 @@ char buffer[64] = {0};
/* single line */ - hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP, - 0, 0, 200, 60, 0, 0, 0, 0); + if (!is_win9x) + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP, + 0, 0, 200, 60, 0, 0, 0, 0); + else + hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP, + 0, 0, 200, 60, 0, 0, 0, 0); ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF; @@ -4978,8 +5195,12 @@ DestroyWindow(hwnd);
/* multi line */ - hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE, - 0, 0, 200, 60, 0, 0, 0, 0); + if (!is_win9x) + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE, + 0, 0, 200, 60, 0, 0, 0, 0); + else + hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE, + 0, 0, 200, 60, 0, 0, 0, 0); ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF; @@ -5051,7 +5272,7 @@ static void test_eventMask(void) { HWND parent; - int ret; + int ret, style; WNDCLASSA cls; const char text[] = "foo bar\n"; int eventMask; @@ -5092,6 +5313,38 @@ ok(queriedEventMask == (eventMask & ~ENM_CHANGE), "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
+ /* check to see if EN_CHANGE is sent when redraw is turned off */ + SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0); + ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n"); + SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0); + /* redraw is disabled by making the window invisible. */ + ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n"); + queriedEventMask = 0; /* initialize to something other than we expect */ + SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text); + ok(queriedEventMask == (eventMask & ~ENM_CHANGE), + "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask); + SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0); + ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n"); + + /* check to see if EN_UPDATE is sent when the editor isn't visible */ + SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0); + style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE); + SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE); + ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n"); + watchForEventMask = EN_UPDATE; + queriedEventMask = 0; /* initialize to something other than we expect */ + SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text); + ok(queriedEventMask == 0, + "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask); + SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style); + ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n"); + queriedEventMask = 0; /* initialize to something other than we expect */ + SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text); + ok(queriedEventMask == eventMask, + "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask); + + + DestroyWindow(parent); }
static int received_WM_NOTIFY = 0; @@ -5167,6 +5420,14 @@ ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n"); ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
+ /* Test for WM_NOTIFY messages with redraw disabled. */ + SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0); + SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0); + received_WM_NOTIFY = 0; + SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted"); + ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n"); + SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0); + DestroyWindow(hwndRichedit_WM_NOTIFY); DestroyWindow(parent); } @@ -5178,8 +5439,12 @@ char buffer[64] = {0};
/* multi-line control inserts CR normally */ - hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE, - 0, 0, 200, 60, 0, 0, 0, 0); + if (!is_win9x) + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE, + 0, 0, 200, 60, 0, 0, 0, 0); + else + hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE, + 0, 0, 200, 60, 0, 0, 0, 0); ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
result = SendMessage(hwnd, EM_CANUNDO, 0, 0); @@ -5221,8 +5486,8 @@ simulate_typing_characters(hwnd, "one two three"); result = SendMessage(hwnd, EM_CANREDO, 0, 0); ok (result == FALSE, "Redo buffer should have been cleared by typing.\n"); - SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0); - SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0); + SendMessage(hwnd, WM_KILLFOCUS, 0, 0); + SendMessage(hwnd, WM_SETFOCUS, 0, 0); simulate_typing_characters(hwnd, " four five six"); result = SendMessage(hwnd, EM_UNDO, 0, 0); ok (result == TRUE, "Failed to undo typed characters.\n"); @@ -5289,6 +5554,43 @@ DestroyWindow(hwnd); }
+static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code) +{ + int length; + + /* MSDN lied, length is actually the number of bytes. */ + length = bytes / sizeof(WCHAR); + switch(code) + { + case WB_ISDELIMITER: + return text[pos] == 'X'; + case WB_LEFT: + case WB_MOVEWORDLEFT: + if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER)) + return pos-1; + return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0); + case WB_LEFTBREAK: + pos--; + while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER)) + pos--; + return pos; + case WB_RIGHT: + case WB_MOVEWORDRIGHT: + if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER)) + return pos+1; + return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length); + case WB_RIGHTBREAK: + pos++; + while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER)) + pos++; + return pos; + default: + ok(FALSE, "Unexpected code %d\n", code); + break; + } + return 0; +} + #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT) #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
@@ -5297,6 +5599,7 @@ HWND hwnd; int result; int sel_start, sel_end; + const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
/* multi-line control inserts CR normally */ hwnd = new_richedit(NULL); @@ -5346,6 +5649,42 @@ ok(sel_start == sel_end, "Selection should be empty\n"); ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
+ /* Test with a custom word break procedure that uses X as the delimiter. */ + result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree"); + ok (result == TRUE, "Failed to clear the text.\n"); + SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc); + /* |one twoXthree */ + SEND_CTRL_RIGHT(hwnd); + /* one twoX|three */ + SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end); + ok(sel_start == sel_end, "Selection should be empty\n"); + ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8); + + DestroyWindow(hwnd); + + /* Make sure the behaviour is the same with a unicode richedit window, + * and using unicode functions. */ + if (is_win9x) + { + skip("Cannot test with unicode richedit window\n"); + return; + } + + hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL, + ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE, + 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL); + + /* Test with a custom word break procedure that uses X as the delimiter. */ + result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW); + ok (result == TRUE, "Failed to clear the text.\n"); + SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc); + /* |one twoXthree */ + SEND_CTRL_RIGHT(hwnd); + /* one twoX|three */ + SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end); + ok(sel_start == sel_end, "Selection should be empty\n"); + ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8); + DestroyWindow(hwnd); }
@@ -5368,15 +5707,318 @@ DestroyWindow(hwnd); }
+static void test_word_wrap(void) +{ + HWND hwnd; + POINTL point = {0, 60}; /* This point must be below the first line */ + const char *text = "Must be long enough to test line wrapping"; + DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE; + int res, pos, lines; + + /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping + * when specified on window creation and set later. */ + hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle, + 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwnd != NULL, "error: %d\n", (int) GetLastError()); + res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text); + ok(res, "WM_SETTEXT failed.\n"); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos); + lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0); + ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines); + + SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos); + DestroyWindow(hwnd); + + hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL, + 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwnd != NULL, "error: %d\n", (int) GetLastError()); + + res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text); + ok(res, "WM_SETTEXT failed.\n"); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos); + lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0); + ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines); + + SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos); + DestroyWindow(hwnd); + + hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL, + 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwnd != NULL, "error: %d\n", (int) GetLastError()); + res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text); + ok(res, "WM_SETTEXT failed.\n"); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos); + + SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos); + DestroyWindow(hwnd); + + hwnd = CreateWindow(RICHEDIT_CLASS, NULL, + dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL, + 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwnd != NULL, "error: %d\n", (int) GetLastError()); + res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text); + ok(res, "WM_SETTEXT failed.\n"); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos); + + SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos); + + /* Test the effect of EM_SETTARGETDEVICE on word wrap. */ + res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1); + todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos); + + res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0); + todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res); + pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point); + ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos); + DestroyWindow(hwnd); + + /* Test to see if wrapping happens with redraw disabled. */ + hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle, + 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwnd != NULL, "error: %d\n", (int) GetLastError()); + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); + res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text); + ok(res, "EM_REPLACESEL failed.\n"); + lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0); + ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines); + MoveWindow(hwnd, 0, 0, 200, 80, FALSE); + lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0); + ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines); + + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + DestroyWindow(hwnd); +} + +static void test_auto_yscroll(void) +{ + HWND hwnd = new_richedit(NULL); + int lines, ret, redraw; + POINT pt; + + for (redraw = 0; redraw <= 1; redraw++) { + trace("testing with WM_SETREDRAW=%d\n", redraw); + SendMessage(hwnd, WM_SETREDRAW, redraw, 0); + SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8"); + lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0); + ok(lines == 8, "%d lines instead of 8\n", lines); + ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt); + ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret); + ok(pt.y != 0, "Didn't scroll down after replacing text.\n"); + ret = GetWindowLong(hwnd, GWL_STYLE); + ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret); + + SendMessage(hwnd, WM_SETTEXT, 0, 0); + lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0); + ok(lines == 1, "%d lines instead of 1\n", lines); + ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt); + ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret); + ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y); + ret = GetWindowLong(hwnd, GWL_STYLE); + ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret); + } + + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + DestroyWindow(hwnd); +} + + +static void test_format_rect(void) +{ + HWND hwnd; + RECT rc, expected, clientRect; + int n; + DWORD options; + + hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL, + ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE, + 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError()); + + GetClientRect(hwnd, &clientRect); + + expected = clientRect; + expected.left += 1; + expected.right -= 1; + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + for (n = -3; n <= 3; n++) + { + rc = clientRect; + rc.top += n; + rc.left += n; + rc.bottom -= n; + rc.right -= n; + SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc); + + expected = rc; + expected.top = max(0, rc.top); + expected.left = max(0, rc.left); + expected.bottom = min(clientRect.bottom, rc.bottom); + expected.right = min(clientRect.right, rc.right); + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + n, rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + } + + rc = clientRect; + SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc); + expected = clientRect; + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + /* Adding the selectionbar adds the selectionbar width to the left side. */ + SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR); + options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0); + ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n"); + expected.left += 8; /* selection bar width */ + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + rc = clientRect; + SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc); + expected = clientRect; + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + /* Removing the selectionbar subtracts the selectionbar width from the left side, + * even if the left side is already 0. */ + SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR); + options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0); + ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n"); + expected.left -= 8; /* selection bar width */ + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + /* Set the absolute value of the formatting rectangle. */ + rc = clientRect; + SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc); + expected = clientRect; + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + n, rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + /* MSDN documents the EM_SETRECT message as using the rectangle provided in + * LPARAM as being a relative offset when the WPARAM value is 1, but these + * tests show that this isn't true. */ + rc.top = 15; + rc.left = 15; + rc.bottom = clientRect.bottom - 15; + rc.right = clientRect.right - 15; + expected = rc; + SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc); + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + /* For some reason it does not limit the values to the client rect with + * a WPARAM value of 1. */ + rc.top = -15; + rc.left = -15; + rc.bottom = clientRect.bottom + 15; + rc.right = clientRect.right + 15; + expected = rc; + SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc); + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + DestroyWindow(hwnd); + + /* The extended window style affects the formatting rectangle. */ + hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL, + ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE, + 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL); + ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError()); + + GetClientRect(hwnd, &clientRect); + + expected = clientRect; + expected.left += 1; + expected.top += 1; + expected.right -= 1; + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + rc = clientRect; + rc.top += 5; + rc.left += 5; + rc.bottom -= 5; + rc.right -= 5; + expected = rc; + expected.top -= 1; + expected.left -= 1; + expected.right += 1; + SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc); + SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc); + ok(rc.top == expected.top && rc.left == expected.left && + rc.bottom == expected.bottom && rc.right == expected.right, + "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n", + rc.top, rc.left, rc.bottom, rc.right, + expected.top, expected.left, expected.bottom, expected.right); + + DestroyWindow(hwnd); +} + START_TEST( editor ) { - MSG msg; - time_t end; - /* Must explicitly LoadLibrary(). The test has no references to functions in * RICHED20.DLL, so the linker doesn't actually link to it. */ hmoduleRichEdit = LoadLibrary("RICHED20.DLL"); ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError()); + + is_win9x = GetVersion() & 0x80000000; + test_WM_CHAR(); test_EM_FINDTEXT(); test_EM_GETLINE(); @@ -5418,22 +6060,15 @@ test_word_movement(); test_EM_CHARFROMPOS(); test_SETPARAFORMAT(); + test_word_wrap(); + test_auto_yscroll(); + test_format_rect();
/* Set the environment variable WINETEST_RICHED20 to keep windows * responsive and open for 30 seconds. This is useful for debugging. - * - * The message pump uses PeekMessage() to empty the queue and then sleeps for - * 50ms before retrying the queue. */ - end = time(NULL) + 30; + */ if (getenv( "WINETEST_RICHED20" )) { - while (time(NULL) < end) { - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } else { - Sleep(50); - } - } + keep_responsive(30); }
OleFlushClipboard();
Modified: trunk/rostests/winetests/riched20/riched20.rbuild URL: http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/riched20/riched2... ============================================================================== --- trunk/rostests/winetests/riched20/riched20.rbuild [iso-8859-1] (original) +++ trunk/rostests/winetests/riched20/riched20.rbuild [iso-8859-1] Mon Dec 29 02:27:52 2008 @@ -7,6 +7,7 @@ <file>editor.c</file> <file>richole.c</file> <file>testlist.c</file> + <file>txtsrv.c</file> <library>wine</library> <library>uuid</library> <library>ole32</library>
Modified: trunk/rostests/winetests/riched20/testlist.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/riched20/testlis... ============================================================================== --- trunk/rostests/winetests/riched20/testlist.c [iso-8859-1] (original) +++ trunk/rostests/winetests/riched20/testlist.c [iso-8859-1] Mon Dec 29 02:27:52 2008 @@ -6,8 +6,12 @@ #define STANDALONE #include "wine/test.h"
+extern void func_editor(void); +extern void func_txtsrv(void);
const struct test winetest_testlist[] = { + { "editor", func_editor }, + { "txtsrv", func_txtsrv }, { 0, 0 } };
Added: trunk/rostests/winetests/riched20/txtsrv.c URL: http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/riched20/txtsrv.... ============================================================================== --- trunk/rostests/winetests/riched20/txtsrv.c (added) +++ trunk/rostests/winetests/riched20/txtsrv.c [iso-8859-1] Mon Dec 29 02:27:52 2008 @@ -1,0 +1,662 @@ +/* + * Unit test suite for windowless rich edit controls + * + * Copyright 2008 Maarten Lankhorst + * Copyright 2008 Austin Lund + * Copyright 2008 Dylan Smith + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include <stdio.h> +#include <stdarg.h> +#include <windef.h> +#include <winbase.h> +#include <objbase.h> +#include <richedit.h> +#include <initguid.h> +#include <textserv.h> +#include <wine/test.h> + +static HMODULE hmoduleRichEdit; + +/* Define C Macros for ITextServices calls. */ + +/* Use a special table for x86 machines to convert the thiscall + * calling convention. This isn't needed on other platforms. */ +#ifdef __i386__ +#define TXTSERV_VTABLE(This) (&itextServicesStdcallVtbl) +#else /* __i386__ */ +#define TXTSERV_VTABLE(This) (This)->lpVtbl +#endif /* __i386__ */ + +#define ITextServices_TxSendMessage(This,a,b,c,d) TXTSERV_VTABLE(This)->TxSendMessage(This,a,b,c,d) +#define ITextServices_TxDraw(This,a,b,c,d,e,f,g,h,i,j,k,l) TXTSERV_VTABLE(This)->TxDraw(This,a,b,c,d,e,f,g,h,i,j,k,l) +#define ITextServices_TxGetHScroll(This,a,b,c,d,e) TXTSERV_VTABLE(This)->TxGetHScroll(This,a,b,c,d,e) +#define ITextServices_TxGetVScroll(This,a,b,c,d,e) TXTSERV_VTABLE(This)->TxGetVScroll(This,a,b,c,d,e) +#define ITextServices_OnTxSetCursor(This,a,b,c,d,e,f,g,h,i) TXTSERV_VTABLE(This)->OnTxSetCursor(This,a,b,c,d,e,f,g,h,i) +#define ITextServices_TxQueryHitPoint(This,a,b,c,d,e,f,g,h,i,j) TXTSERV_VTABLE(This)->TxQueryHitPoint(This,a,b,c,d,e,f,g,h,i,j) +#define ITextServices_OnTxInplaceActivate(This,a) TXTSERV_VTABLE(This)->OnTxInplaceActivate(This,a) +#define ITextServices_OnTxInplaceDeactivate(This) TXTSERV_VTABLE(This)->OnTxInplaceDeactivate(This) +#define ITextServices_OnTxUIActivate(This) TXTSERV_VTABLE(This)->OnTxUIActivate(This) +#define ITextServices_OnTxUIDeactivate(This) TXTSERV_VTABLE(This)->OnTxUIDeactivate(This) +#define ITextServices_TxGetText(This,a) TXTSERV_VTABLE(This)->TxGetText(This,a) +#define ITextServices_TxSetText(This,a) TXTSERV_VTABLE(This)->TxSetText(This,a) +#define ITextServices_TxGetCurrentTargetX(This,a) TXTSERV_VTABLE(This)->TxGetCurrentTargetX(This,a) +#define ITextServices_TxGetBaseLinePos(This,a) TXTSERV_VTABLE(This)->TxGetBaseLinePos(This,a) +#define ITextServices_TxGetNaturalSize(This,a,b,c,d,e,f,g,h) TXTSERV_VTABLE(This)->TxGetNaturalSize(This,a,b,c,d,e,f,g,h) +#define ITextServices_TxGetDropTarget(This,a) TXTSERV_VTABLE(This)->TxGetDropTarget(This,a) +#define ITextServices_OnTxPropertyBitsChange(This,a,b) TXTSERV_VTABLE(This)->OnTxPropertyBitsChange(This,a,b) +#define ITextServices_TxGetCachedSize(This,a,b) TXTSERV_VTABLE(This)->TxGetCachedSize(This,a,b) + +/* Set the WINETEST_DEBUG environment variable to be greater than 1 for verbose + * function call traces of ITextHost. */ +#define TRACECALL if(winetest_debug > 1) trace + +/************************************************************************/ +/* ITextHost implementation for conformance testing. */ + +typedef struct ITextHostTestImpl +{ + ITextHostVtbl *lpVtbl; + LONG refCount; +} ITextHostTestImpl; + +static HRESULT WINAPI ITextHostImpl_QueryInterface(ITextHost *iface, + REFIID riid, + LPVOID *ppvObject) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ITextHost)) { + *ppvObject = This; + ITextHost_AddRef((ITextHost *)*ppvObject); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI ITextHostImpl_AddRef(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + ULONG refCount = InterlockedIncrement(&This->refCount); + return refCount; +} + +static ULONG WINAPI ITextHostImpl_Release(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + ULONG refCount = InterlockedDecrement(&This->refCount); + + if (!refCount) + { + CoTaskMemFree(This); + return 0; + } else { + return refCount; + } +} + +static HDC WINAPI ITextHostImpl_TxGetDC(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetDC(%p)\n", This); + return NULL; +} + +static INT WINAPI ITextHostImpl_TxReleaseDC(ITextHost *iface, + HDC hdc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxReleaseDC(%p)\n", This); + return 0; +} + +static BOOL WINAPI ITextHostImpl_TxShowScrollBar(ITextHost *iface, + INT fnBar, + BOOL fShow) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxShowScrollBar(%p, fnBar=%d, fShow=%d)\n", + This, fnBar, fShow); + return FALSE; +} + +static BOOL WINAPI ITextHostImpl_TxEnableScrollBar(ITextHost *iface, + INT fuSBFlags, + INT fuArrowflags) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxEnableScrollBar(%p, fuSBFlags=%d, fuArrowflags=%d)\n", + This, fuSBFlags, fuArrowflags); + return FALSE; +} + +static BOOL WINAPI ITextHostImpl_TxSetScrollRange(ITextHost *iface, + INT fnBar, + LONG nMinPos, + INT nMaxPos, + BOOL fRedraw) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetScrollRange(%p, fnBar=%d, nMinPos=%d, nMaxPos=%d, fRedraw=%d)\n", + This, fnBar, nMinPos, nMaxPos, fRedraw); + return FALSE; +} + +static BOOL WINAPI ITextHostImpl_TxSetScrollPos(ITextHost *iface, + INT fnBar, + INT nPos, + BOOL fRedraw) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetScrollPos(%p, fnBar=%d, nPos=%d, fRedraw=%d)\n", + This, fnBar, nPos, fRedraw); + return FALSE; +} + +static void WINAPI ITextHostImpl_TxInvalidateRect(ITextHost *iface, + LPCRECT prc, + BOOL fMode) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxInvalidateRect(%p, prc=%p, fMode=%d)\n", + This, prc, fMode); +} + +static void WINAPI ITextHostImpl_TxViewChange(ITextHost *iface, BOOL fUpdate) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxViewChange(%p, fUpdate=%d)\n", + This, fUpdate); +} + +static BOOL WINAPI ITextHostImpl_TxCreateCaret(ITextHost *iface, + HBITMAP hbmp, + INT xWidth, INT yHeight) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxCreateCaret(%p, nbmp=%p, xWidth=%d, yHeight=%d)\n", + This, hbmp, xWidth, yHeight); + return FALSE; +} + +static BOOL WINAPI ITextHostImpl_TxShowCaret(ITextHost *iface, BOOL fShow) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxShowCaret(%p, fShow=%d)\n", + This, fShow); + return FALSE; +} + +static BOOL WINAPI ITextHostImpl_TxSetCaretPos(ITextHost *iface, + INT x, INT y) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetCaretPos(%p, x=%d, y=%d)\n", This, x, y); + return FALSE; +} + +static BOOL WINAPI ITextHostImpl_TxSetTimer(ITextHost *iface, + UINT idTimer, UINT uTimeout) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetTimer(%p, idTimer=%u, uTimeout=%u)\n", + This, idTimer, uTimeout); + return FALSE; +} + +static void WINAPI ITextHostImpl_TxKillTimer(ITextHost *iface, UINT idTimer) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxKillTimer(%p, idTimer=%u)\n", This, idTimer); +} + +static void WINAPI ITextHostImpl_TxScrollWindowEx(ITextHost *iface, + INT dx, INT dy, + LPCRECT lprcScroll, + LPCRECT lprcClip, + HRGN hRgnUpdate, + LPRECT lprcUpdate, + UINT fuScroll) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxScrollWindowEx(%p, %d, %d, %p, %p, %p, %p, %d)\n", + This, dx, dy, lprcScroll, lprcClip, hRgnUpdate, lprcUpdate, fuScroll); +} + +static void WINAPI ITextHostImpl_TxSetCapture(ITextHost *iface, BOOL fCapture) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetCapture(%p, fCapture=%d)\n", This, fCapture); +} + +static void WINAPI ITextHostImpl_TxSetFocus(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetFocus(%p)\n", This); +} + +static void WINAPI ITextHostImpl_TxSetCursor(ITextHost *iface, + HCURSOR hcur, + BOOL fText) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxSetCursor(%p, hcur=%p, fText=%d)\n", + This, hcur, fText); +} + +static BOOL WINAPI ITextHostImpl_TxScreenToClient(ITextHost *iface, + LPPOINT lppt) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxScreenToClient(%p, lppt=%p)\n", This, lppt); + return FALSE; +} + +static BOOL WINAPI ITextHostImpl_TxClientToScreen(ITextHost *iface, + LPPOINT lppt) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxClientToScreen(%p, lppt=%p)\n", This, lppt); + return FALSE; +} + +static HRESULT WINAPI ITextHostImpl_TxActivate(ITextHost *iface, + LONG *plOldState) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxActivate(%p, plOldState=%p)\n", This, plOldState); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxDeactivate(ITextHost *iface, + LONG lNewState) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxDeactivate(%p, lNewState=%d)\n", This, lNewState); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetClientRect(ITextHost *iface, + LPRECT prc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetClientRect(%p, prc=%p)\n", This, prc); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetViewInset(ITextHost *iface, + LPRECT prc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetViewInset(%p, prc=%p)\n", This, prc); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetCharFormat(ITextHost *iface, + const CHARFORMATW **ppCF) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetCharFormat(%p, ppCF=%p)\n", This, ppCF); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetParaFormat(ITextHost *iface, + const PARAFORMAT **ppPF) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetParaFormat(%p, ppPF=%p)\n", This, ppPF); + return E_NOTIMPL; +} + +static COLORREF WINAPI ITextHostImpl_TxGetSysColor(ITextHost *iface, + int nIndex) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetSysColor(%p, nIndex=%d)\n", This, nIndex); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetBackStyle(ITextHost *iface, + TXTBACKSTYLE *pStyle) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetBackStyle(%p, pStyle=%p)\n", This, pStyle); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetMaxLength(ITextHost *iface, + DWORD *pLength) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetMaxLength(%p, pLength=%p)\n", This, pLength); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetScrollBars(ITextHost *iface, + DWORD *pdwScrollBar) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetScrollBars(%p, pdwScrollBar=%p)\n", + This, pdwScrollBar); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetPasswordChar(ITextHost *iface, + WCHAR *pch) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetPasswordChar(%p, pch=%p)\n", This, pch); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetAcceleratorPos(ITextHost *iface, + LONG *pch) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetAcceleratorPos(%p, pch=%p)\n", This, pch); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_TxGetExtent(ITextHost *iface, + LPSIZEL lpExtent) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetExtent(%p, lpExtent=%p)\n", This, lpExtent); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_OnTxCharFormatChange(ITextHost *iface, + const CHARFORMATW *pcf) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to OnTxCharFormatChange(%p, pcf=%p)\n", This, pcf); + return E_NOTIMPL; +} + +static HRESULT WINAPI ITextHostImpl_OnTxParaFormatChange(ITextHost *iface, + const PARAFORMAT *ppf) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to OnTxParaFormatChange(%p, ppf=%p)\n", This, ppf); + return E_NOTIMPL; +} + +/* This must return S_OK for the native ITextServices object to + initialize. */ +static HRESULT WINAPI ITextHostImpl_TxGetPropertyBits(ITextHost *iface, + DWORD dwMask, + DWORD *pdwBits) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetPropertyBits(%p, dwMask=0x%08x, pdwBits=%p)\n", + This, dwMask, pdwBits); + *pdwBits = 0; + return S_OK; +} + +static HRESULT WINAPI ITextHostImpl_TxNotify(ITextHost *iface, DWORD iNotify, + void *pv) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxNotify(%p, iNotify=%d, pv=%p)\n", This, iNotify, pv); + return E_NOTIMPL; +} + +static HIMC WINAPI ITextHostImpl_TxImmGetContext(ITextHost *iface) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxImmGetContext(%p)\n", This); + return 0; +} + +static void WINAPI ITextHostImpl_TxImmReleaseContext(ITextHost *iface, HIMC himc) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxImmReleaseContext(%p, himc=%p)\n", This, himc); +} + +static HRESULT WINAPI ITextHostImpl_TxGetSelectionBarWidth(ITextHost *iface, + LONG *lSelBarWidth) +{ + ITextHostTestImpl *This = (ITextHostTestImpl *)iface; + TRACECALL("Call to TxGetSelectionBarWidth(%p, lSelBarWidth=%p)\n", + This, lSelBarWidth); + return E_NOTIMPL; +} + +static ITextServicesVtbl itextServicesStdcallVtbl; + +static ITextHostVtbl itextHostVtbl = { + ITextHostImpl_QueryInterface, + ITextHostImpl_AddRef, + ITextHostImpl_Release, + ITextHostImpl_TxGetDC, + ITextHostImpl_TxReleaseDC, + ITextHostImpl_TxShowScrollBar, + ITextHostImpl_TxEnableScrollBar, + ITextHostImpl_TxSetScrollRange, + ITextHostImpl_TxSetScrollPos, + ITextHostImpl_TxInvalidateRect, + ITextHostImpl_TxViewChange, + ITextHostImpl_TxCreateCaret, + ITextHostImpl_TxShowCaret, + ITextHostImpl_TxSetCaretPos, + ITextHostImpl_TxSetTimer, + ITextHostImpl_TxKillTimer, + ITextHostImpl_TxScrollWindowEx, + ITextHostImpl_TxSetCapture, + ITextHostImpl_TxSetFocus, + ITextHostImpl_TxSetCursor, + ITextHostImpl_TxScreenToClient, + ITextHostImpl_TxClientToScreen, + ITextHostImpl_TxActivate, + ITextHostImpl_TxDeactivate, + ITextHostImpl_TxGetClientRect, + ITextHostImpl_TxGetViewInset, + ITextHostImpl_TxGetCharFormat, + ITextHostImpl_TxGetParaFormat, + ITextHostImpl_TxGetSysColor, + ITextHostImpl_TxGetBackStyle, + ITextHostImpl_TxGetMaxLength, + ITextHostImpl_TxGetScrollBars, + ITextHostImpl_TxGetPasswordChar, + ITextHostImpl_TxGetAcceleratorPos, + ITextHostImpl_TxGetExtent, + ITextHostImpl_OnTxCharFormatChange, + ITextHostImpl_OnTxParaFormatChange, + ITextHostImpl_TxGetPropertyBits, + ITextHostImpl_TxNotify, + ITextHostImpl_TxImmGetContext, + ITextHostImpl_TxImmReleaseContext, + ITextHostImpl_TxGetSelectionBarWidth +}; + +static ITextServices *txtserv = NULL; +static ITextHostTestImpl *dummyTextHost; +static void *wrapperCodeMem = NULL; + +#include "pshpack1.h" + +/* Code structure for x86 byte code */ +typedef struct +{ + BYTE pop_eax; /* popl %eax */ + BYTE push_ecx; /* pushl %ecx */ + BYTE push_eax; /* pushl %eax */ + BYTE jmp_func; /* jmp $func */ + DWORD func; +} THISCALL_TO_STDCALL_THUNK; + +typedef struct +{ + BYTE pop_eax; /* popl %eax */ + BYTE pop_ecx; /* popl %ecx */ + BYTE push_eax; /* pushl %eax */ + BYTE mov_vtable_eax[2]; /* movl (%ecx), %eax */ + BYTE jmp_eax[2]; /* jmp *$vtablefunc_offset(%eax) */ + int vtablefunc_offset; +} STDCALL_TO_THISCALL_THUNK; + +#include "poppack.h" + +static void setup_thiscall_wrappers(void) +{ +#ifdef __i386__ + void** pVtable; + void** pVtableEnd; + THISCALL_TO_STDCALL_THUNK *thunk; + STDCALL_TO_THISCALL_THUNK *thunk2; + + wrapperCodeMem = VirtualAlloc(NULL, + (sizeof(ITextHostVtbl)/sizeof(void*) - 3) + * sizeof(THISCALL_TO_STDCALL_THUNK) + +(sizeof(ITextServicesVtbl)/sizeof(void*) - 3) + * sizeof(STDCALL_TO_THISCALL_THUNK), + MEM_COMMIT, PAGE_EXECUTE_READWRITE); + thunk = wrapperCodeMem; + + /* Wrap all ITextHostImpl methods with code to perform a thiscall to + * stdcall conversion. The thiscall calling convention places the This + * pointer in ecx on the x86 platform, and the stdcall calling convention + * pushes the This pointer on the stack as the first argument. + * + * The byte code does the conversion then jumps to the real function. + * + * Each wrapper needs to be modified so that the function to jump to is + * modified in the byte code. */ + + /* Skip QueryInterface, AddRef, and Release native actually + * defined them with the stdcall calling convention. */ + pVtable = (void**)&itextHostVtbl + 3; + pVtableEnd = (void**)(&itextHostVtbl + 1); + while (pVtable != pVtableEnd) { + /* write byte code to executable memory */ + thunk->pop_eax = 0x58; /* popl %eax */ + thunk->push_ecx = 0x51; /* pushl %ecx */ + thunk->push_eax = 0x50; /* pushl %eax */ + thunk->jmp_func = 0xe9; /* jmp $func */ + /* The address needs to be relative to the end of the jump instructions. */ + thunk->func = (char*)*pVtable - (char*)(&thunk->func + 1); + *pVtable = thunk; + pVtable++; + thunk++; + } + + /* Setup an ITextServices standard call vtable that will call the + * native thiscall vtable when the methods are called. */ + + /* QueryInterface, AddRef, and Release should be called directly on the + * real vtable since they use the stdcall calling convention. */ + thunk2 = (STDCALL_TO_THISCALL_THUNK *)thunk; + pVtable = (void**)&itextServicesStdcallVtbl + 3; + pVtableEnd = (void**)(&itextServicesStdcallVtbl + 1); + while (pVtable != pVtableEnd) { + /* write byte code to executable memory */ + thunk2->pop_eax = 0x58; /* popl %eax */ + thunk2->pop_ecx = 0x59; /* popl %ecx */ + thunk2->push_eax = 0x50; /* pushl %eax */ + thunk2->mov_vtable_eax[0] = 0x8b; /* movl (%ecx), %eax */ + thunk2->mov_vtable_eax[1] = 0x01; + thunk2->jmp_eax[0] = 0xff; /* jmp *$vtablefunc_offset(%eax) */ + thunk2->jmp_eax[1] = 0xa0; + thunk2->vtablefunc_offset = (char*)pVtable - (char*)&itextServicesStdcallVtbl; + *pVtable = thunk2; + pVtable++; + thunk2++; + } +#endif /* __i386__ */ +} + +/*************************************************************************/ +/* Conformance test functions. */ + +/* Initialize the test texthost structure */ +static BOOL init_texthost(void) +{ + IUnknown *init; + HRESULT result; + PCreateTextServices pCreateTextServices; + + dummyTextHost = CoTaskMemAlloc(sizeof(*dummyTextHost)); + if (dummyTextHost == NULL) { + skip("Insufficient memory to create ITextHost interface\n"); + return FALSE; + } + dummyTextHost->lpVtbl = &itextHostVtbl; + dummyTextHost->refCount = 1; + + /* MSDN states that an IUnknown object is returned by + CreateTextServices which is then queried to obtain a + ITextServices object. */ + pCreateTextServices = (void*)GetProcAddress(hmoduleRichEdit, "CreateTextServices"); + result = (*pCreateTextServices)(NULL,(ITextHost*)dummyTextHost, &init); + ok(result == S_OK, "Did not return OK when created. Returned %x\n", result); + if (result != S_OK) { + CoTaskMemFree(dummyTextHost); + skip("CreateTextServices failed.\n"); + return FALSE; + } + + result = IUnknown_QueryInterface(init, &IID_ITextServices, + (void **)&txtserv); + ok((result == S_OK) && (txtserv != NULL), "Querying interface failed\n"); + IUnknown_Release(init); + if (!((result == S_OK) && (txtserv != NULL))) { + CoTaskMemFree(dummyTextHost); + skip("Could not retrieve ITextServices interface\n"); + return FALSE; + } + + return TRUE; +} + +static void test_TxGetText(void) +{ + HRESULT hres; + BSTR rettext; + + if (!init_texthost()) + return; + + hres = ITextServices_TxGetText(txtserv, &rettext); + todo_wine ok(hres == S_OK, "ITextServices_TxGetText failed\n"); + + IUnknown_Release(txtserv); + CoTaskMemFree(dummyTextHost); +} + +START_TEST( txtsrv ) +{ + setup_thiscall_wrappers(); + + /* Must explicitly LoadLibrary(). The test has no references to functions in + * RICHED20.DLL, so the linker doesn't actually link to it. */ + hmoduleRichEdit = LoadLibrary("RICHED20.DLL"); + ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError()); + + if (init_texthost()) + { + IUnknown_Release(txtserv); + CoTaskMemFree(dummyTextHost); + + test_TxGetText(); + } + if (wrapperCodeMem) VirtualFree(wrapperCodeMem, 0, MEM_RELEASE); +}
Propchange: trunk/rostests/winetests/riched20/txtsrv.c ------------------------------------------------------------------------------ svn:eol-style = native