https://git.reactos.org/?p=reactos.git;a=commitdiff;h=364d6e0346e62d6e9ece700be1adac76be763d68

commit 364d6e0346e62d6e9ece700be1adac76be763d68
Author:     Doug Lyons <dougly...@douglyons.com>
AuthorDate: Sat Feb 8 13:04:04 2025 -0600
Commit:     GitHub <nore...@github.com>
CommitDate: Sat Feb 8 13:04:04 2025 -0600

    [RICHED20_WINETEST] Sync to Wine-6.10. (#7692)
    
    ROSTESTS-398 and CORE-6727
---
 modules/rostests/winetests/riched20/editor.c  |  73 ++-
 modules/rostests/winetests/riched20/richole.c | 890 +++++++++++++++-----------
 modules/rostests/winetests/riched20/txtsrv.c  | 285 ++++++++-
 sdk/include/psdk/textserv.h                   |  88 ++-
 4 files changed, 915 insertions(+), 421 deletions(-)

diff --git a/modules/rostests/winetests/riched20/editor.c 
b/modules/rostests/winetests/riched20/editor.c
index 791f471dee2..6bac7e0a89b 100644
--- a/modules/rostests/winetests/riched20/editor.c
+++ b/modules/rostests/winetests/riched20/editor.c
@@ -33,6 +33,8 @@
 #include <ole2.h>
 #include <richedit.h>
 #include <richole.h>
+#include <imm.h>
+#include <textserv.h>
 #include <commdlg.h>
 #include <time.h>
 #include <wine/test.h>
@@ -51,12 +53,38 @@ static CHAR string1[MAX_PATH], string2[MAX_PATH], 
string3[MAX_PATH];
 static HMODULE hmoduleRichEdit;
 static BOOL is_lang_japanese;
 
+#if defined(__i386__) && !defined(__MINGW32__) && (!defined(_MSC_VER) || 
!defined(__clang__))
+static void disable_beep( HWND hwnd )
+{
+    /* don't attempt to disable beep if we don't have thiscall compiler 
support */
+}
+#else
+#define ITextServices_OnTxPropertyBitsChange(This,a,b) 
(This)->lpVtbl->OnTxPropertyBitsChange(This,a,b)
+static void disable_beep( HWND hwnd )
+{
+    IRichEditOle *richole;
+    ITextServices *services;
+    IID *pIID_ITextServices = (IID *)GetProcAddress( hmoduleRichEdit, 
"IID_ITextServices" );
+
+    if (SendMessageW( hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&richole ))
+    {
+        if (SUCCEEDED( IRichEditOle_QueryInterface( richole, 
pIID_ITextServices, (void **)&services ) ))
+        {
+            ITextServices_OnTxPropertyBitsChange( services, TXTBIT_ALLOWBEEP, 
0 );
+            ITextServices_Release( services );
+        }
+        IRichEditOle_Release( richole );
+    }
+}
+#endif
+
 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
   HWND hwnd;
   hwnd = CreateWindowA(lpClassName, NULL, 
dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
                       hmoduleRichEdit, NULL);
   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) 
GetLastError());
+  disable_beep( hwnd );
   return hwnd;
 }
 
@@ -66,6 +94,7 @@ static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, 
HWND parent) {
                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
                       hmoduleRichEdit, NULL);
   ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) 
GetLastError());
+  disable_beep( hwnd );
   return hwnd;
 }
 
@@ -615,7 +644,7 @@ static void test_EM_POSFROMCHAR(void)
       "gg\n"
       "hh\n";
 
-  rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
+  rtl = (GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT, LOCALE_FONTSIGNATURE,
                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
          (sig.lsUsb[3] & 0x08000000) != 0);
 
@@ -772,7 +801,7 @@ static void test_EM_SETCHARFORMAT(void)
   BOOL rtl;
   DWORD expect_effects;
 
-  rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
+  rtl = (GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT, LOCALE_FONTSIGNATURE,
                         (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
          (sig.lsUsb[3] & 0x08000000) != 0);
 
@@ -1791,8 +1820,8 @@ static void test_EM_GETTEXTRANGE(void)
         textRange.chrg.cpMin = 4;
         textRange.chrg.cpMax = 8;
         result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, 
(LPARAM)&textRange);
-        todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
-        todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled 
%s\n", buffer);
+        ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
+        ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", 
buffer);
     }
 
     DestroyWindow(hwndRichEdit);
@@ -1829,8 +1858,8 @@ static void test_EM_GETSELTEXT(void)
         SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 
(LPARAM)"abcdef\x8e\xf0ghijk");
         SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
         result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
-        todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
-        todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled 
%s\n", buffer);
+        ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
+        ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", 
buffer);
     }
 
     DestroyWindow(hwndRichEdit);
@@ -1863,6 +1892,7 @@ static void test_EM_SETOPTIONS(void)
                                 hmoduleRichEdit, NULL);
     ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
        RICHEDIT_CLASS20A, (int) GetLastError());
+    disable_beep( hwndRichEdit );
     options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
     /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
     ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
@@ -3110,11 +3140,9 @@ static void test_scrollbar_visibility(void)
   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
     "Vertical scrollbar is invisible, should be visible.\n");
-  todo_wine {
   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
         si.nPage, si.nMin, si.nMax);
-  }
 
   /* Ditto, see above */
   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
@@ -3124,11 +3152,9 @@ static void test_scrollbar_visibility(void)
   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
     "Vertical scrollbar is invisible, should be visible.\n");
-  todo_wine {
   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
         si.nPage, si.nMin, si.nMax);
-  }
 
   /* Ditto, see above */
   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
@@ -3138,11 +3164,9 @@ static void test_scrollbar_visibility(void)
   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
     "Vertical scrollbar is invisible, should be visible.\n");
-  todo_wine {
   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
         si.nPage, si.nMin, si.nMax);
-  }
 
   /* Ditto, see above */
   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
@@ -3152,11 +3176,9 @@ static void test_scrollbar_visibility(void)
   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
     "Vertical scrollbar is invisible, should be visible.\n");
-  todo_wine {
   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
         si.nPage, si.nMin, si.nMax);
-  }
 
   /* Ditto, see above */
   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
@@ -3166,11 +3188,9 @@ static void test_scrollbar_visibility(void)
   GetScrollInfo(hwndRichEdit, SB_VERT, &si);
   ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
     "Vertical scrollbar is invisible, should be visible.\n");
-  todo_wine {
   ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
         "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
         si.nPage, si.nMin, si.nMax);
-  }
 
   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
   SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
@@ -5610,7 +5630,7 @@ static void test_EM_FORMATRANGE(void)
     {"WINE\r\n\r\nwine\r\nwine", 5, 6}
   };
 
-  skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
+  skip_non_english = (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_ENGLISH);
   if (skip_non_english)
     skip("Skipping some tests on non-English platform\n");
 
@@ -6348,6 +6368,7 @@ static void test_WM_CHAR(void)
     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
                            0, 0, 200, 60, 0, 0, 0, 0);
     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
+    disable_beep( hwnd );
 
     p = char_list;
     while (*p != '\0') {
@@ -6951,6 +6972,7 @@ static void test_undo_coalescing(void)
     hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
                            0, 0, 200, 60, 0, 0, 0, 0);
     ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
+    disable_beep( hwnd );
 
     result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
     ok (result == FALSE, "Can undo after window creation.\n");
@@ -7461,12 +7483,25 @@ static void test_format_rect(void)
     ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
        wine_dbgstr_rect(&expected));
 
+    /* reset back to client rect and now try adding selection bar */
+    SendMessageA(hwnd, EM_SETRECT, 0, 0);
+    expected = clientRect;
+    InflateRect(&expected, -1, 0);
+    SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
+    ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
+       wine_dbgstr_rect(&expected));
+    SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
+    SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
+    ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
+       wine_dbgstr_rect(&expected));
+    SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
+
     /* 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(EqualRect(&rc, &expected), "[n=%d] rect %s != %s\n", n, 
wine_dbgstr_rect(&rc),
+    ok(EqualRect(&rc, &expected), "rect %s != %s\n", wine_dbgstr_rect(&rc),
        wine_dbgstr_rect(&expected));
 
     /* MSDN documents the EM_SETRECT message as using the rectangle provided in
@@ -9026,7 +9061,7 @@ START_TEST( editor )
    * RICHED20.DLL, so the linker doesn't actually link to it. */
   hmoduleRichEdit = LoadLibraryA("riched20.dll");
   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
-  is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
+  is_lang_japanese = (PRIMARYLANGID(GetSystemDefaultLangID()) == 
LANG_JAPANESE);
 
   test_window_classes();
   test_WM_CHAR();
diff --git a/modules/rostests/winetests/riched20/richole.c 
b/modules/rostests/winetests/riched20/richole.c
index 492bdc3fa9a..3885eb527fc 100644
--- a/modules/rostests/winetests/riched20/richole.c
+++ b/modules/rostests/winetests/riched20/richole.c
@@ -149,6 +149,7 @@ static void test_Interfaces(void)
   LRESULT res;
   HWND w;
   ULONG refcount;
+  IUnknown *unk, *unk2;
 
   w = new_richedit(NULL);
   if (!w) {
@@ -180,6 +181,14 @@ static void test_Interfaces(void)
   hres = ITextDocument_GetSelection(txtDoc, &txtSel);
   ok(hres == S_OK, "got 0x%08x\n", hres);
 
+  hres = ITextDocument_QueryInterface(txtDoc, &IID_IUnknown, (void **)&unk);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  hres = ITextSelection_QueryInterface(txtSel, &IID_IUnknown, (void **)&unk2);
+  ok(hres == S_OK, "got 0x%08x\n", hres);
+  ok(unk != unk2, "unknowns are the same\n");
+  IUnknown_Release(unk2);
+  IUnknown_Release(unk);
+
   EXPECT_REF(txtDoc, 4);
   EXPECT_REF(txtSel, 2);
 
@@ -3203,29 +3212,62 @@ static void fill_reobject_struct(REOBJECT *reobj, LONG 
cp, LPOLEOBJECT poleobj,
   reobj->dwUser = user;
 }
 
-#define CHECK_REOBJECT_STRUCT(reobj,poleobj,pstg,polesite,user) \
-  _check_reobject_struct(reobj, poleobj, pstg, polesite, user, __LINE__)
-static void _check_reobject_struct(REOBJECT reobj, LPOLEOBJECT poleobj, 
LPSTORAGE pstg,
-                                  LPOLECLIENTSITE polesite, DWORD user, int 
line)
+#define CHECK_REOBJECT_STRUCT(reole,index,flags,cp,poleobj,pstg,polesite,user) 
\
+  _check_reobject_struct(reole, index, flags, cp, poleobj, pstg, polesite, 
user, __LINE__)
+static void _check_reobject_struct(IRichEditOle *reole, LONG index, DWORD 
flags, LONG cp,
+    LPOLEOBJECT poleobj, LPSTORAGE pstg, LPOLECLIENTSITE polesite, DWORD user, 
int line)
 {
+  REOBJECT reobj;
+  HRESULT hr;
+
+  reobj.cbStruct = sizeof(reobj);
+  reobj.cp = cp;
+  hr = IRichEditOle_GetObject(reole, index, &reobj, flags);
+  ok(hr == S_OK, "IRichEditOle_GetObject failed: %#x.\n", hr);
   ok_(__FILE__,line)(reobj.poleobj == poleobj, "got wrong object 
interface.\n");
   ok_(__FILE__,line)(reobj.pstg == pstg, "got wrong storage interface.\n");
   ok_(__FILE__,line)(reobj.polesite == polesite, "got wrong site 
interface.\n");
   ok_(__FILE__,line)(reobj.dwUser == user, "got wrong user-defined value.\n");
 }
 
+#define INSERT_REOBJECT(reole,reobj,cp,user) \
+  _insert_reobject(reole, reobj, cp, user, __LINE__)
+static void _insert_reobject(IRichEditOle *reole, REOBJECT *reobj, LONG cp, 
DWORD user, int line)
+{
+  IOleClientSite *clientsite;
+  HRESULT hr;
+  hr = IRichEditOle_GetClientSite(reole, &clientsite);
+  ok_(__FILE__,line)(hr == S_OK, "IRichEditOle_GetClientSite got hr %#x.\n", 
hr);
+  fill_reobject_struct(reobj, cp, NULL, NULL, clientsite, 10, 10, 
DVASPECT_CONTENT, 0, user);
+  hr = IRichEditOle_InsertObject(reole, reobj);
+  ok_(__FILE__,line)(hr == S_OK, "IRichEditOle_InsertObject got hr %#x.\n", 
hr);
+  IOleClientSite_Release(clientsite);
+}
+
 static void test_InsertObject(void)
 {
   static CHAR test_text1[] = "abcdefg";
   IRichEditOle *reole = NULL;
   ITextDocument *doc = NULL;
-  IOleClientSite *clientsite;
-  REOBJECT reo1, reo2, reo3, received_reo1, received_reo2, received_reo3, 
received_reo4;
+  REOBJECT reo1, reo2, reo3, received_reo;
   HRESULT hr;
   HWND hwnd;
-  LONG count;
+  const WCHAR *expected_string;
+  const CHAR *expected_stringA;
+  ITextSelection *selection;
+  IDataObject *dataobject;
+  TEXTRANGEA textrange;
+  FORMATETC formatetc;
+  CHARRANGE charrange;
+  GETTEXTEX gettextex;
+  STGMEDIUM stgmedium;
+  WCHAR buffer[1024];
+  CHAR bufferA[1024];
+  LONG count, result;
+  ITextRange *range;
+  BSTR bstr;
 
-  create_interfaces(&hwnd, &reole, &doc, NULL);
+  create_interfaces(&hwnd, &reole, &doc, &selection);
   SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_text1);
 
   hr = IRichEditOle_InsertObject(reole, NULL);
@@ -3233,161 +3275,278 @@ static void test_InsertObject(void)
 
   /* insert object1 in (0, 1)*/
   SendMessageA(hwnd, EM_SETSEL, 0, 1);
-  hr = IRichEditOle_GetClientSite(reole, &clientsite);
-  ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
-  fill_reobject_struct(&reo1, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 
10, DVASPECT_CONTENT, 0, 1);
-  hr = IRichEditOle_InsertObject(reole, &reo1);
-  ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
+  INSERT_REOBJECT(reole, &reo1, REO_CP_SELECTION, 1);
   count = IRichEditOle_GetObjectCount(reole);
   ok(count == 1, "got wrong object count: %d\n", count);
-  IOleClientSite_Release(clientsite);
 
   /* insert object2 in (2, 3)*/
   SendMessageA(hwnd, EM_SETSEL, 2, 3);
-  hr = IRichEditOle_GetClientSite(reole, &clientsite);
-  ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
-  fill_reobject_struct(&reo2, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 
10, DVASPECT_CONTENT, 0, 2);
-  hr = IRichEditOle_InsertObject(reole, &reo2);
-  ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
+  INSERT_REOBJECT(reole, &reo2, REO_CP_SELECTION, 2);
   count = IRichEditOle_GetObjectCount(reole);
   ok(count == 2, "got wrong object count: %d\n", count);
-  IOleClientSite_Release(clientsite);
 
   /* insert object3 in (1, 2)*/
   SendMessageA(hwnd, EM_SETSEL, 1, 2);
-  hr = IRichEditOle_GetClientSite(reole, &clientsite);
-  ok(hr == S_OK, "IRichEditOle_GetClientSite failed: 0x%08x\n", hr);
-  fill_reobject_struct(&reo3, REO_CP_SELECTION, NULL, NULL, clientsite, 10, 
10, DVASPECT_CONTENT, 0, 3);
-  hr = IRichEditOle_InsertObject(reole, &reo3);
-  ok(hr == S_OK, "IRichEditOle_InsertObject failed: 0x%08x\n", hr);
+  INSERT_REOBJECT(reole, &reo3, REO_CP_SELECTION, 3);
   count = IRichEditOle_GetObjectCount(reole);
   ok(count == 3, "got wrong object count: %d\n", count);
-  IOleClientSite_Release(clientsite);
 
   /* tests below show that order of rebject (from 0 to 2) is: reo1,reo3,reo2 */
-  received_reo1.cbStruct = sizeof(received_reo1);
-  hr = IRichEditOle_GetObject(reole, 0, &received_reo1, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo1, NULL, NULL, reo1.polesite, 1);
-
-  received_reo2.cbStruct = sizeof(received_reo2);
-  hr = IRichEditOle_GetObject(reole, 1, &received_reo2, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo2, NULL, NULL, reo3.polesite, 3);
-
-  received_reo3.cbStruct = sizeof(received_reo3);
-  hr = IRichEditOle_GetObject(reole, 2, &received_reo3, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo3, NULL, NULL, reo2.polesite, 2);
+  CHECK_REOBJECT_STRUCT(reole, 0, REO_GETOBJ_ALL_INTERFACES, 0, NULL, NULL, 
reo1.polesite, 1);
+  CHECK_REOBJECT_STRUCT(reole, 1, REO_GETOBJ_ALL_INTERFACES, 0, NULL, NULL, 
reo3.polesite, 3);
+  CHECK_REOBJECT_STRUCT(reole, 2, REO_GETOBJ_ALL_INTERFACES, 0, NULL, NULL, 
reo2.polesite, 2);
 
   hr = IRichEditOle_GetObject(reole, 2, NULL, REO_GETOBJ_ALL_INTERFACES);
   ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
 
-  received_reo4.cbStruct = 0;
-  hr = IRichEditOle_GetObject(reole, 2, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
+  received_reo.cbStruct = 0;
+  hr = IRichEditOle_GetObject(reole, 2, &received_reo, 
REO_GETOBJ_ALL_INTERFACES);
   ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
 
-  received_reo4.cbStruct = sizeof(received_reo4);
-  hr = IRichEditOle_GetObject(reole, 2, &received_reo4, REO_GETOBJ_PSTG);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, NULL, 2);
-
-  hr = IRichEditOle_GetObject(reole, 2, &received_reo4, REO_GETOBJ_POLESITE);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo2.polesite, 2);
+  CHECK_REOBJECT_STRUCT(reole, 2, REO_GETOBJ_PSTG, 0, NULL, NULL, NULL, 2);
+  CHECK_REOBJECT_STRUCT(reole, 2, REO_GETOBJ_POLESITE, 0, NULL, NULL, 
reo2.polesite, 2);
 
-  hr = IRichEditOle_GetObject(reole, 4, &received_reo4, REO_GETOBJ_POLESITE);
+  hr = IRichEditOle_GetObject(reole, 3, &received_reo, REO_GETOBJ_POLESITE);
   ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
 
-  hr = IRichEditOle_GetObject(reole, 1024, &received_reo4, 
REO_GETOBJ_POLESITE);
+  hr = IRichEditOle_GetObject(reole, 4, &received_reo, REO_GETOBJ_POLESITE);
   ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
 
-  /* received_reo4 will be zeroed before be used */
-  hr = IRichEditOle_GetObject(reole, 2, &received_reo4, 
REO_GETOBJ_NO_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, NULL, 2);
+  hr = IRichEditOle_GetObject(reole, 1024, &received_reo, REO_GETOBJ_POLESITE);
+  ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
 
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_USE_CP, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo1.polesite, 1);
+  hr = IRichEditOle_GetObject(reole, -10, &received_reo, REO_GETOBJ_POLESITE);
+  ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
 
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 1;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_USE_CP, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
+  /* received_reo will be zeroed before be used */
+  received_reo.cbStruct = sizeof(received_reo);
+  received_reo.polesite = (IOleClientSite *)0xdeadbeef;
+  hr = IRichEditOle_GetObject(reole, 2, &received_reo, 
REO_GETOBJ_NO_INTERFACES);
   ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo3.polesite, 3);
+  ok(received_reo.polesite == (IOleClientSite *)NULL, "Got wrong site 
interface.\n");
 
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 2;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_USE_CP, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo2.polesite, 2);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_USE_CP, REO_GETOBJ_ALL_INTERFACES, 0, 
NULL, NULL, reo1.polesite, 1);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_USE_CP, REO_GETOBJ_ALL_INTERFACES, 1, 
NULL, NULL, reo3.polesite, 3);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_USE_CP, REO_GETOBJ_ALL_INTERFACES, 2, 
NULL, NULL, reo2.polesite, 2);
 
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 4;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_USE_CP, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
+  received_reo.cbStruct = sizeof(received_reo);
+  received_reo.polesite = (IOleClientSite *)0xdeadbeef;
+  received_reo.dwUser = 4;
+  received_reo.cp = 4;
+  hr = IRichEditOle_GetObject(reole, REO_IOB_USE_CP, &received_reo, 
REO_GETOBJ_ALL_INTERFACES);
   ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
-  /* received_reo4 didn't be zeroed in E_INVALIDARG case */
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo2.polesite, 2);
+  ok(received_reo.polesite == (IOleClientSite *)0xdeadbeef, "Got wrong site 
interface.\n");
+  ok(received_reo.dwUser == 4, "Got wrong user-defined value: %d.\n", 
received_reo.dwUser);
 
   SendMessageA(hwnd, EM_SETSEL, 0, 1);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 1;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo1.polesite, 1);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, REO_GETOBJ_ALL_INTERFACES, 
0, NULL, NULL, reo1.polesite, 1);
 
   SendMessageA(hwnd, EM_SETSEL, 1, 2);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo3.polesite, 3);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, REO_GETOBJ_ALL_INTERFACES, 
0, NULL, NULL, reo3.polesite, 3);
 
   SendMessageA(hwnd, EM_SETSEL, 2, 3);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo2.polesite, 2);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, REO_GETOBJ_ALL_INTERFACES, 
0, NULL, NULL, reo2.polesite, 2);
 
   SendMessageA(hwnd, EM_SETSEL, 0, 2);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo1.polesite, 1);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, REO_GETOBJ_ALL_INTERFACES, 
0, NULL, NULL, reo1.polesite, 1);
 
   SendMessageA(hwnd, EM_SETSEL, 1, 3);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo3.polesite, 3);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, REO_GETOBJ_ALL_INTERFACES, 
0, NULL, NULL, reo3.polesite, 3);
 
   SendMessageA(hwnd, EM_SETSEL, 2, 0);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo1.polesite, 1);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, REO_GETOBJ_ALL_INTERFACES, 
0, NULL, NULL, reo1.polesite, 1);
 
   SendMessageA(hwnd, EM_SETSEL, 0, 6);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
-  ok(hr == S_OK, "IRichEditOle_GetObject failed: 0x%08x\n", hr);
-  CHECK_REOBJECT_STRUCT(received_reo4, NULL, NULL, reo1.polesite, 1);
+  CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, REO_GETOBJ_ALL_INTERFACES, 
0, NULL, NULL, reo1.polesite, 1);
 
   SendMessageA(hwnd, EM_SETSEL, 4, 5);
-  received_reo4.cbStruct = sizeof(received_reo4);
-  received_reo4.cp = 0;
-  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo4, 
REO_GETOBJ_ALL_INTERFACES);
+  received_reo.cbStruct = sizeof(received_reo);
+  received_reo.cp = 0;
+  hr = IRichEditOle_GetObject(reole, REO_IOB_SELECTION, &received_reo, 
REO_GETOBJ_ALL_INTERFACES);
   ok(hr == E_INVALIDARG, "IRichEditOle_GetObject should fail: 0x%08x\n", hr);
 
-  release_interfaces(&hwnd, &reole, &doc, NULL);
+  SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_text1);
+
+  /* "abc|d|efg" */
+  INSERT_REOBJECT(reole, &reo1, 3, 1);
+  INSERT_REOBJECT(reole, &reo2, 5, 2);
+
+  SendMessageW(hwnd, EM_SETSEL, 2, 3);
+  result = SendMessageW(hwnd, EM_SELECTIONTYPE, 0, 0);
+  ok(result == SEL_TEXT, "Got selection type: %x.\n", result);
+
+  SendMessageW(hwnd, EM_SETSEL, 3, 4);
+  result = SendMessageW(hwnd, EM_SELECTIONTYPE, 0, 0);
+  todo_wine ok(result == SEL_OBJECT, "Got selection type: %x.\n", result);
+  todo_wine CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, 
REO_GETOBJ_ALL_INTERFACES, 1, NULL, NULL, reo1.polesite, 1);
+
+  SendMessageW(hwnd, EM_SETSEL, 2, 4);
+  result = SendMessageW(hwnd, EM_SELECTIONTYPE, 0, 0);
+  todo_wine ok(result == (SEL_TEXT | SEL_OBJECT), "Got selection type: %x.\n", 
result);
+
+  SendMessageW(hwnd, EM_SETSEL, 5, 6);
+  todo_wine CHECK_REOBJECT_STRUCT(reole, REO_IOB_SELECTION, 
REO_GETOBJ_ALL_INTERFACES, 1, NULL, NULL, reo2.polesite, 2);
+
+#ifdef __REACTOS__
+  expected_string = L"abc\xfffc"L"d\xfffc"L"efg";
+#else
+  expected_string = L"abc\xfffc""d\xfffc""efg";
+#endif
+
+  gettextex.cb = sizeof(buffer);
+  gettextex.flags = GT_DEFAULT;
+  gettextex.codepage = 1200;
+  gettextex.lpDefaultChar = NULL;
+  gettextex.lpUsedDefChar = NULL;
+  result = SendMessageW(hwnd, EM_GETTEXTEX, (WPARAM)&gettextex, 
(LPARAM)buffer);
+  ok(result == lstrlenW(expected_string), "Got wrong length: %d.\n", result);
+  todo_wine ok(!lstrcmpW(buffer, expected_string), "Got wrong content: %s.\n", 
debugstr_w(buffer));
+
+  gettextex.flags = GT_RAWTEXT;
+  memset(buffer, 0, sizeof(buffer));
+  result = SendMessageW(hwnd, EM_GETTEXTEX, (WPARAM)&gettextex, 
(LPARAM)buffer);
+  ok(result == lstrlenW(expected_string), "Got wrong length: %d.\n", result);
+  todo_wine ok(!lstrcmpW(buffer, expected_string), "Got wrong content: %s.\n", 
debugstr_w(buffer));
+
+  expected_stringA = "abc d efg";
+  memset(bufferA, 0, sizeof(bufferA));
+  SendMessageA(hwnd, EM_SETSEL, 0, -1);
+  result = SendMessageA(hwnd, EM_GETSELTEXT, (WPARAM)sizeof(bufferA), 
(LPARAM)bufferA);
+  ok(result == strlen(expected_stringA), "Got wrong length: %d.\n", result);
+  todo_wine ok(!strcmp(bufferA, expected_stringA), "Got wrong content: %s.\n", 
bufferA);
+
+  memset(bufferA, 0, sizeof(bufferA));
+  textrange.lpstrText = bufferA;
+  textrange.chrg.cpMin = 0;
+  textrange.chrg.cpMax = 11;
+  result = SendMessageA(hwnd, EM_GETTEXTRANGE, 0, (LPARAM)&textrange);
+  ok(result == strlen(expected_stringA), "Got wrong length: %d.\n", result);
+  todo_wine ok(!strcmp(bufferA, expected_stringA), "Got wrong content: %s.\n", 
bufferA);
+
+#ifdef __REACTOS__
+  expected_string = L"abc\xfffc"L"d\xfffc"L"efg\r";
+#else
+  expected_string = L"abc\xfffc""d\xfffc""efg\r";
+#endif
+
+  hr = ITextDocument_Range(doc, 0, 11, &range);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  hr = ITextRange_GetText(range, &bstr);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  ok(lstrlenW(bstr) == lstrlenW(expected_string), "Got wrong length: %d.\n", 
lstrlenW(bstr));
+  todo_wine ok(!lstrcmpW(bstr, expected_string), "Got text: %s.\n", 
wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+  hr = ITextRange_SetRange(range, 3, 4);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  hr = ITextRange_GetChar(range, &result);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  todo_wine ok(result == 0xfffc, "Got char: %c\n", result);
+  ITextRange_Release(range);
+
+  SendMessageW(hwnd, EM_SETSEL, 0, -1);
+  hr = ITextSelection_GetText(selection, &bstr);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  ok(lstrlenW(bstr) == lstrlenW(expected_string), "Got wrong length: %d.\n", 
lstrlenW(bstr));
+  todo_wine ok(!lstrcmpW(bstr, expected_string), "Got text: %s.\n", 
wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+  SendMessageW(hwnd, EM_SETSEL, 3, 4);
+  result = 0;
+  hr = ITextSelection_GetChar(selection, &result);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  todo_wine ok(result == 0xfffc, "Got char: %c\n", result);
+
+  SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
+  result = SendMessageW(hwnd, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
+  ok(!result, "Got result %x.\n", result);
+  /* "abc|d|efg" */
+  SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_text1);
+  INSERT_REOBJECT(reole, &reo1, 3, 1);
+  INSERT_REOBJECT(reole, &reo2, 5, 2);
+
+  expected_string = L"abc d efg";
+  charrange.cpMin = 0;
+  charrange.cpMax = 11;
+  hr = IRichEditOle_GetClipboardData(reole, &charrange, 1, &dataobject);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  formatetc.cfFormat = CF_UNICODETEXT;
+  formatetc.dwAspect = DVASPECT_CONTENT;
+  formatetc.ptd = NULL;
+  formatetc.tymed = TYMED_HGLOBAL;
+  formatetc.lindex = -1;
+  hr = IDataObject_GetData(dataobject, &formatetc, &stgmedium);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  todo_wine ok(lstrlenW(stgmedium.hGlobal) == lstrlenW(expected_string), "Got 
wrong length: %d.\n", result);
+  todo_wine ok(!lstrcmpW(stgmedium.hGlobal, expected_string), "Got wrong 
content: %s.\n", debugstr_w(stgmedium.hGlobal));
+
+#ifdef __REACTOS__
+  expected_string = L"abc\xfffc"L"d\xfffc"L"efg";
+#else
+  expected_string = L"abc\xfffc""d\xfffc""efg";
+#endif
+
+  gettextex.cb = sizeof(buffer);
+  gettextex.flags = GT_DEFAULT;
+  gettextex.codepage = 1200;
+  gettextex.lpDefaultChar = NULL;
+  gettextex.lpUsedDefChar = NULL;
+  result = SendMessageW(hwnd, EM_GETTEXTEX, (WPARAM)&gettextex, 
(LPARAM)buffer);
+  ok(result == lstrlenW(expected_string), "Got wrong length: %d.\n", result);
+  todo_wine ok(!lstrcmpW(buffer, expected_string), "Got wrong content: %s.\n", 
debugstr_w(buffer));
+
+  gettextex.flags = GT_RAWTEXT;
+  memset(buffer, 0, sizeof(buffer));
+  result = SendMessageW(hwnd, EM_GETTEXTEX, (WPARAM)&gettextex, 
(LPARAM)buffer);
+  ok(result == lstrlenW(expected_string), "Got wrong length: %d.\n", result);
+  todo_wine ok(!lstrcmpW(buffer, expected_string), "Got wrong content: %s.\n", 
debugstr_w(buffer));
+
+  expected_stringA = "abc d efg";
+  memset(bufferA, 0, sizeof(bufferA));
+  SendMessageA(hwnd, EM_SETSEL, 0, -1);
+  result = SendMessageA(hwnd, EM_GETSELTEXT, (WPARAM)sizeof(bufferA), 
(LPARAM)bufferA);
+  ok(result == strlen(expected_stringA), "Got wrong length: %d.\n", result);
+  todo_wine ok(!strcmp(bufferA, expected_stringA), "Got wrong content: %s.\n", 
bufferA);
+
+  memset(bufferA, 0, sizeof(bufferA));
+  textrange.lpstrText = bufferA;
+  textrange.chrg.cpMin = 0;
+  textrange.chrg.cpMax = 11;
+  result = SendMessageA(hwnd, EM_GETTEXTRANGE, 0, (LPARAM)&textrange);
+  ok(result == strlen(expected_stringA), "Got wrong length: %d.\n", result);
+  todo_wine ok(!strcmp(bufferA, expected_stringA), "Got wrong content: %s.\n", 
bufferA);
+
+#ifdef __REACTOS__
+  expected_string = L"abc\xfffc"L"d\xfffc"L"efg";
+#else
+  expected_string = L"abc\xfffc""d\xfffc""efg";
+#endif
+
+  hr = ITextDocument_Range(doc, 0, 11, &range);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  hr = ITextRange_GetText(range, &bstr);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  todo_wine ok(lstrlenW(bstr) == lstrlenW(expected_string), "Got wrong length: 
%d.\n", lstrlenW(bstr));
+  todo_wine ok(!lstrcmpW(bstr, expected_string), "Got text: %s.\n", 
wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+  hr = ITextRange_SetRange(range, 3, 4);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  hr = ITextRange_GetChar(range, &result);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  todo_wine ok(result == 0xfffc, "Got char: %c\n", result);
+  ITextRange_Release(range);
+
+  SendMessageW(hwnd, EM_SETSEL, 0, -1);
+  hr = ITextSelection_GetText(selection, &bstr);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  todo_wine ok(lstrlenW(bstr) == lstrlenW(expected_string), "Got wrong length: 
%d.\n", lstrlenW(bstr));
+  todo_wine ok(!lstrcmpW(bstr, expected_string), "Got text: %s.\n", 
wine_dbgstr_w(bstr));
+  SysFreeString(bstr);
+  SendMessageW(hwnd, EM_SETSEL, 3, 4);
+  result = 0;
+  hr = ITextSelection_GetChar(selection, &result);
+  ok(hr == S_OK, "Got hr %#x.\n", hr);
+  todo_wine ok(result == 0xfffc, "Got char: %c\n", result);
+
+  release_interfaces(&hwnd, &reole, &doc, &selection);
 }
 
 static void test_GetStoryLength(void)
@@ -3694,7 +3853,7 @@ static void test_Expand(void)
   ITextRange_Release(range);
 }
 
-static void test_MoveEnd(void)
+static void test_MoveEnd_story(void)
 {
   static const char test_text1[] = "Word1 Word2";
   IRichEditOle *reole = NULL;
@@ -3808,272 +3967,288 @@ static void test_MoveEnd(void)
   ITextRange_Release(range);
 }
 
-static void test_ITextRange_SetStart(void)
+static void test_character_movestart(ITextRange *range, int textlen, int i, 
int j, LONG target)
 {
-  HWND w;
-  IRichEditOle *reOle = NULL;
-  ITextDocument *txtDoc = NULL;
-  ITextRange *txtRge = NULL;
-  HRESULT hres;
-  LONG first, lim, start, end;
-  static const CHAR test_text1[] = "TestSomeText";
-
-  create_interfaces(&w, &reOle, &txtDoc, NULL);
-  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
-
-  first = 4, lim = 8;
-  ITextDocument_Range(txtDoc, first, lim, &txtRge);
-  hres = ITextRange_SetStart(txtRge, first);
-  ok(hres == S_FALSE, "ITextRange_SetStart\n");
-
-#define TEST_TXTRGE_SETSTART(cp, expected_start, expected_end)  \
-  hres = ITextRange_SetStart(txtRge, cp);                       \
-  ok(hres == S_OK, "ITextRange_SetStart\n");                    \
-  ITextRange_GetStart(txtRge, &start);                          \
-  ITextRange_GetEnd(txtRge, &end);                              \
-  ok(start == expected_start, "got wrong start value: %d\n", start);  \
-  ok(end == expected_end, "got wrong end value: %d\n", end);
-
-  TEST_TXTRGE_SETSTART(2, 2, 8)
-  TEST_TXTRGE_SETSTART(-1, 0, 8)
-  TEST_TXTRGE_SETSTART(13, 12, 12)
-
-  release_interfaces(&w, &reOle, &txtDoc, NULL);
+    HRESULT hr;
+    LONG delta = 0;
+    LONG expected_delta;
+    LONG expected_start = target;
+
+    if (expected_start < 0)
+        expected_start = 0;
+    else if (expected_start > textlen)
+        expected_start = textlen;
+    expected_delta = expected_start - i;
+    hr = ITextRange_SetRange(range, i, j);
+    ok(SUCCEEDED(hr), "got 0x%08x\n", hr);
+    hr = ITextRange_MoveStart(range, tomCharacter, target - i, &delta);
+    if (expected_start == i) {
+        ok(hr == S_FALSE, "(%d,%d) move by %d got hr=0x%08x\n", i, j, target - 
i, hr);
+        ok(delta == 0, "(%d,%d) move by %d got delta %d\n", i, j, target - i, 
delta);
+        CHECK_RANGE(range, i, j);
+    } else {
+        ok(hr == S_OK, "(%d,%d) move by %d got hr=0x%08x\n", i, j, target - i, 
hr);
+        ok(delta == expected_delta, "(%d,%d) move by %d got delta %d\n", i, j, 
target - i, delta);
+        if (expected_start <= j)
+            CHECK_RANGE(range, expected_start, j);
+        else
+            CHECK_RANGE(range, expected_start, expected_start);
+    }
 }
 
-static void test_ITextRange_SetEnd(void)
+static void test_character_moveend(ITextRange *range, int textlen, int i, int 
j, LONG target)
 {
-  HWND w;
-  IRichEditOle *reOle = NULL;
-  ITextDocument *txtDoc = NULL;
-  ITextRange *txtRge = NULL;
-  HRESULT hres;
-  LONG first, lim, start, end;
-  static const CHAR test_text1[] = "TestSomeText";
-
-  create_interfaces(&w, &reOle, &txtDoc, NULL);
-  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
-
-  first = 4, lim = 8;
-  ITextDocument_Range(txtDoc, first, lim, &txtRge);
-  hres = ITextRange_SetEnd(txtRge, lim);
-  ok(hres == S_FALSE, "ITextRange_SetEnd\n");
-
-#define TEST_TXTRGE_SETEND(cp, expected_start, expected_end)    \
-  hres = ITextRange_SetEnd(txtRge, cp);                         \
-  ok(hres == S_OK, "ITextRange_SetEnd\n");                      \
-  ITextRange_GetStart(txtRge, &start);                          \
-  ITextRange_GetEnd(txtRge, &end);                              \
-  ok(start == expected_start, "got wrong start value: %d\n", start);  \
-  ok(end == expected_end, "got wrong end value: %d\n", end);
-
-  TEST_TXTRGE_SETEND(6, 4, 6)
-  TEST_TXTRGE_SETEND(14, 4, 13)
-  TEST_TXTRGE_SETEND(-1, 0, 0)
-
-  ITextRange_Release(txtRge);
-  release_interfaces(&w, &reOle, &txtDoc, NULL);
+    HRESULT hr;
+    LONG delta;
+    LONG expected_delta;
+    LONG expected_end = target;
+
+    if (expected_end < 0)
+        expected_end = 0;
+    else if (expected_end > textlen + 1)
+        expected_end = textlen + 1;
+    expected_delta = expected_end - j;
+    hr = ITextRange_SetRange(range, i, j);
+    ok(SUCCEEDED(hr), "got 0x%08x\n", hr);
+    hr = ITextRange_MoveEnd(range, tomCharacter, target - j, &delta);
+    if (expected_end == j) {
+        ok(hr == S_FALSE, "(%d,%d) move by %d got hr=0x%08x\n", i, j, target - 
j, hr);
+        ok(delta == 0, "(%d,%d) move by %d got delta %d\n", i, j, target - j, 
delta);
+        CHECK_RANGE(range, i, j);
+    } else {
+        ok(hr == S_OK, "(%d,%d) move by %d got hr=0x%08x\n", i, j, target - j, 
hr);
+        ok(delta == expected_delta, "(%d,%d) move by %d got delta %d\n", i, j, 
target - j, delta);
+        if (i <= expected_end)
+            CHECK_RANGE(range, i, expected_end);
+        else
+            CHECK_RANGE(range, expected_end, expected_end);
+    }
 }
 
-static void test_ITextSelection_SetStart(void)
+static void test_character_move(ITextRange *range, int textlen, int i, int j, 
LONG target)
 {
-  HWND w;
-  IRichEditOle *reOle = NULL;
-  ITextDocument *txtDoc = NULL;
-  ITextSelection *txtSel = NULL;
-  HRESULT hres;
-  LONG first, lim, start, end;
-  static const CHAR test_text1[] = "TestSomeText";
-
-  create_interfaces(&w, &reOle, &txtDoc, &txtSel);
-  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
-
-  first = 4, lim = 8;
-  SendMessageA(w, EM_SETSEL, first, lim);
-  hres = ITextSelection_SetStart(txtSel, first);
-  ok(hres == S_FALSE, "ITextSelection_SetStart\n");
-
-#define TEST_TXTSEL_SETSTART(cp, expected_start, expected_end)        \
-  hres = ITextSelection_SetStart(txtSel, cp);                         \
-  ok(hres == S_OK, "ITextSelection_SetStart\n");                      \
-  SendMessageA(w, EM_GETSEL, (LPARAM)&start, (WPARAM)&end);           \
-  ok(start == expected_start, "got wrong start value: %d\n", start);  \
-  ok(end == expected_end, "got wrong end value: %d\n", end);
-
-  TEST_TXTSEL_SETSTART(2, 2, 8)
-  TEST_TXTSEL_SETSTART(-1, 0, 8)
-  TEST_TXTSEL_SETSTART(13, 12, 12)
+    HRESULT hr;
+    LONG move_by;
+    LONG delta = 0;
+    LONG expected_delta;
+    LONG expected_location = target;
+
+    if (expected_location < 0)
+        expected_location = 0;
+    else if (expected_location > textlen)
+        expected_location = textlen;
+
+    if (target <= i) {
+        move_by = target - i;
+        expected_delta = expected_location - i;
+        if (i != j) {
+            --move_by;
+            --expected_delta;
+        }
+    } else if (j <= target) {
+        move_by = target - j;
+        expected_delta = expected_location - j;
+        if (i != j) {
+            ++move_by;
+            ++expected_delta;
+        }
+    } else {
+        /* There's no way to move to a point between start and end: */
+        return;
+    }
 
-  release_interfaces(&w, &reOle, &txtDoc, &txtSel);
+    hr = ITextRange_SetRange(range, i, j);
+    ok(SUCCEEDED(hr), "got 0x%08x\n", hr);
+    hr = ITextRange_Move(range, tomCharacter, move_by, &delta);
+    if (expected_delta == 0) {
+        ok(hr == S_FALSE, "(%d,%d) move by %d got hr=0x%08x\n", i, j, move_by, 
hr);
+        ok(delta == 0, "(%d,%d) move by %d got delta %d\n", i, j, move_by, 
delta);
+        CHECK_RANGE(range, expected_location, expected_location);
+    } else {
+        ok(hr == S_OK, "(%d,%d) move by %d got hr=0x%08x\n", i, j, move_by, 
hr);
+        ok(delta == expected_delta, "(%d,%d) move by %d got delta %d\n", i, j, 
move_by, delta);
+        CHECK_RANGE(range, expected_location, expected_location);
+    }
 }
 
-static void test_ITextSelection_SetEnd(void)
+static void test_character_startof(ITextRange *range, int textlen, int i, int 
j)
 {
-  HWND w;
-  IRichEditOle *reOle = NULL;
-  ITextDocument *txtDoc = NULL;
-  ITextSelection *txtSel = NULL;
-  HRESULT hres;
-  LONG first, lim, start, end;
-  static const CHAR test_text1[] = "TestSomeText";
-
-  create_interfaces(&w, &reOle, &txtDoc, &txtSel);
-  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
-
-  first = 4, lim = 8;
-  SendMessageA(w, EM_SETSEL, first, lim);
-  hres = ITextSelection_SetEnd(txtSel, lim);
-  ok(hres == S_FALSE, "ITextSelection_SetEnd\n");
-
-#define TEST_TXTSEL_SETEND(cp, expected_start, expected_end)          \
-  hres = ITextSelection_SetEnd(txtSel, cp);                           \
-  ok(hres == S_OK, "ITextSelection_SetEnd\n");                        \
-  SendMessageA(w, EM_GETSEL, (LPARAM)&start, (WPARAM)&end);           \
-  ok(start == expected_start, "got wrong start value: %d\n", start);  \
-  ok(end == expected_end, "got wrong end value: %d\n", end);
-
-  TEST_TXTSEL_SETEND(6, 4, 6)
-  TEST_TXTSEL_SETEND(14, 4, 13)
-  TEST_TXTSEL_SETEND(-1, 0, 0)
-
-  release_interfaces(&w, &reOle, &txtDoc, &txtSel);
+    HRESULT hr;
+    LONG delta;
+
+    hr = ITextRange_SetRange(range, i, j);
+    ok(SUCCEEDED(hr), "got 0x%08x\n", hr);
+    hr = ITextRange_StartOf(range, tomCharacter, tomMove, &delta);
+    if (i == j) {
+        ok(hr == S_FALSE, "(%d,%d) tomMove got hr=0x%08x\n", i, j, hr);
+        ok(delta == 0, "(%d,%d) tomMove got delta %d\n", i, j, delta);
+    } else {
+        ok(hr == S_OK, "(%d,%d) tomMove got hr=0x%08x\n", i, j, hr);
+        ok(delta == -1, "(%d,%d) tomMove got delta %d\n", i, j, delta);
+    }
+    CHECK_RANGE(range, i, i);
+
+    hr = ITextRange_SetRange(range, i, j);
+    ok(SUCCEEDED(hr), "got 0x%08x\n", hr);
+    hr = ITextRange_StartOf(range, tomCharacter, tomExtend, &delta);
+    ok(hr == S_FALSE, "(%d,%d) tomExtend got hr=0x%08x\n", i, j, hr);
+    ok(delta == 0, "(%d,%d) tomExtend got delta %d\n", i, j, delta);
+    CHECK_RANGE(range, i, j);
 }
 
-static void test_ITextRange_GetFont(void)
+static void test_character_endof(ITextRange *range, int textlen, int i, int j)
 {
-  HWND w;
-  IRichEditOle *reOle = NULL;
-  ITextDocument *txtDoc = NULL;
-  ITextRange *txtRge = NULL;
-  ITextFont *txtFont = NULL, *txtFont1 = NULL;
-  HRESULT hres;
-  int first, lim;
-  int refcount;
-  static const CHAR test_text1[] = "TestSomeText";
-  LONG value;
-
-  create_interfaces(&w, &reOle, &txtDoc, NULL);
-  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
-
-  first = 4, lim = 4;
-  ITextDocument_Range(txtDoc, first, lim, &txtRge);
-  refcount = get_refcount((IUnknown *)txtRge);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
-
-  hres = ITextRange_GetFont(txtRge, &txtFont);
-  ok(hres == S_OK, "ITextRange_GetFont\n");
-  refcount = get_refcount((IUnknown *)txtFont);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
-  refcount = get_refcount((IUnknown *)txtRge);
-  ok(refcount == 2, "got wrong ref count: %d\n", refcount);
-
-  hres = ITextRange_GetFont(txtRge, &txtFont1);
-  ok(hres == S_OK, "ITextRange_GetFont\n");
-  ok(txtFont1 != txtFont, "A new pointer should be return\n");
-  refcount = get_refcount((IUnknown *)txtFont1);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
-  ITextFont_Release(txtFont1);
-  refcount = get_refcount((IUnknown *)txtRge);
-  ok(refcount == 2, "got wrong ref count: %d\n", refcount);
-
-  ITextRange_Release(txtRge);
-  release_interfaces(&w, &reOle, &txtDoc, NULL);
-
-  hres = ITextFont_GetOutline(txtFont, &value);
-  ok(hres == CO_E_RELEASED, "ITextFont after ITextDocument destroyed\n");
-
-  ITextFont_Release(txtFont);
+    HRESULT hr;
+    LONG end;
+    LONG delta;
+
+    hr = ITextRange_SetRange(range, i, j);
+    ok(SUCCEEDED(hr), "got 0x%08x\n", hr);
+    hr = ITextRange_EndOf(range, tomCharacter, tomMove, &delta);
+
+    /* A character "end", apparently cannot be before the very first character 
*/
+    end = j;
+    if (j == 0)
+        ++end;
+
+    if (i == end) {
+        ok(hr == S_FALSE, "(%d,%d) tomMove got hr=0x%08x\n", i, j, hr);
+        ok(delta == 0, "(%d,%d) tomMove got delta %d\n", i, j, delta);
+    } else {
+        ok(hr == S_OK, "(%d,%d) tomMove got hr=0x%08x\n", i, j, hr);
+        ok(delta == 1, "(%d,%d) tomMove got delta %d\n", i, j, delta);
+    }
+    CHECK_RANGE(range, end, end);
+
+    hr = ITextRange_SetRange(range, i, j);
+    ok(SUCCEEDED(hr), "got 0x%08x\n", hr);
+    hr = ITextRange_EndOf(range, tomCharacter, tomExtend, &delta);
+    if (0 < j) {
+        ok(hr == S_FALSE, "(%d,%d) tomExtend got hr=0x%08x\n", i, j, hr);
+        ok(delta == 0, "(%d,%d) tomExtend got delta %d\n", i, j, delta);
+    } else {
+        ok(hr == S_OK, "(%d,%d) tomExtend got hr=0x%08x\n", i, j, hr);
+        ok(delta == 1, "(%d,%d) tomExtend got delta %d\n", i, j, delta);
+    }
+    CHECK_RANGE(range, i, end);
 }
 
-static void test_ITextSelection_GetFont(void)
+static void test_character_movement(void)
 {
-  HWND w;
-  IRichEditOle *reOle = NULL;
-  ITextDocument *txtDoc = NULL;
-  ITextSelection *txtSel = NULL;
-  ITextFont *txtFont = NULL, *txtFont1 = NULL;
-  HRESULT hres;
-  int first, lim;
-  int refcount;
-  static const CHAR test_text1[] = "TestSomeText";
-  LONG value;
-
-  create_interfaces(&w, &reOle, &txtDoc, &txtSel);
-  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
-
-  first = 4, lim = 4;
-  SendMessageA(w, EM_SETSEL, first, lim);
-  refcount = get_refcount((IUnknown *)txtSel);
-  ok(refcount == 2, "got wrong ref count: %d\n", refcount);
-
-  hres = ITextSelection_GetFont(txtSel, &txtFont);
-  ok(hres == S_OK, "ITextSelection_GetFont\n");
-  refcount = get_refcount((IUnknown *)txtFont);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
-  refcount = get_refcount((IUnknown *)txtSel);
-  ok(refcount == 3, "got wrong ref count: %d\n", refcount);
+  static const char test_text1[] = "ab\n c";
+  IRichEditOle *reole = NULL;
+  ITextDocument *doc = NULL;
+  ITextRange *range;
+  ITextSelection *selection;
+  HRESULT hr;
+  HWND hwnd;
+  int i, j;
+  const int textlen = strlen(test_text1);
 
-  hres = ITextSelection_GetFont(txtSel, &txtFont1);
-  ok(hres == S_OK, "ITextSelection_GetFont\n");
-  ok(txtFont1 != txtFont, "A new pointer should be return\n");
-  refcount = get_refcount((IUnknown *)txtFont1);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
-  ITextFont_Release(txtFont1);
-  refcount = get_refcount((IUnknown *)txtSel);
-  ok(refcount == 3, "got wrong ref count: %d\n", refcount);
+  create_interfaces(&hwnd, &reole, &doc, &selection);
+  SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_text1);
 
-  release_interfaces(&w, &reOle, &txtDoc, &txtSel);
+  hr = ITextDocument_Range(doc, 0, 0, &range);
+  ok(hr == S_OK, "got 0x%08x\n", hr);
 
-  hres = ITextFont_GetOutline(txtFont, &value);
-  ok(hres == CO_E_RELEASED, "ITextFont after ITextDocument destroyed\n");
+  /* Exhaustive test of every possible combination of (start,end) locations,
+   * against every possible target location to move to. */
+  for (i = 0; i <= textlen; i++) {
+      for (j = i; j <= textlen; j++) {
+          LONG target;
+          for (target = -2; target <= textlen + 3; target++) {
+              test_character_moveend(range, textlen, i, j, target);
+              test_character_movestart(range, textlen, i, j, target);
+              test_character_move(range, textlen, i, j, target);
+          }
+          test_character_startof(range, textlen, i, j);
+          test_character_endof(range, textlen, i, j);
+      }
+  }
 
-  ITextFont_Release(txtFont);
+  release_interfaces(&hwnd, &reole, &doc, NULL);
+  ITextSelection_Release(selection);
+  ITextRange_Release(range);
 }
 
-static void test_ITextRange_GetPara(void)
+#define CLIPBOARD_RANGE_CONTAINS(range, start, end, expected) 
_clipboard_range_contains(range, start, end, expected, __LINE__, 0);
+#define TODO_CLIPBOARD_RANGE_CONTAINS(range, start, end, expected) 
_clipboard_range_contains(range, start, end, expected, __LINE__, 1);
+static void _clipboard_range_contains(ITextRange *range, LONG start, LONG end, 
const char *expected, int line, int todo)
 {
-  HWND w;
-  IRichEditOle *reOle = NULL;
-  ITextDocument *txtDoc = NULL;
-  ITextRange *txtRge = NULL;
-  ITextPara *txtPara = NULL, *txtPara1 = NULL;
-  HRESULT hres;
-  int first, lim;
-  int refcount;
-  static const CHAR test_text1[] = "TestSomeText";
-  LONG value;
-
-  create_interfaces(&w, &reOle, &txtDoc, NULL);
-  SendMessageA(w, WM_SETTEXT, 0, (LPARAM)test_text1);
-
-  first = 4, lim = 4;
-  ITextDocument_Range(txtDoc, first, lim, &txtRge);
-  refcount = get_refcount((IUnknown *)txtRge);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
+  HRESULT hr;
+  BOOL clipboard_open;
+  HGLOBAL global;
+  const char *clipboard_text;
+
+  hr = ITextRange_SetRange(range, start, end);
+  ok_(__FILE__,line)(SUCCEEDED(hr), "SetRange failed: 0x%08x\n", hr);
+  hr = ITextRange_Copy(range, NULL);
+  ok_(__FILE__,line)(hr == S_OK, "Copy failed: 0x%08x\n", hr);
+
+  clipboard_open = OpenClipboard(NULL);
+  ok_(__FILE__,line)(clipboard_open, "OpenClipboard failed: %d\n", 
GetLastError());
+  global = GetClipboardData(CF_TEXT);
+  ok_(__FILE__,line)(global != NULL, "GetClipboardData failed: %p\n", global);
+  clipboard_text = GlobalLock(global);
+  ok_(__FILE__,line)(clipboard_text != NULL, "GlobalLock failed: %p\n", 
clipboard_text);
+#ifdef __REACTOS__
+  if (expected != NULL && clipboard_text != NULL)
+    todo_wine_if(todo) ok_(__FILE__,line)(!strcmp(expected, clipboard_text), 
"unexpected contents: %s\n", wine_dbgstr_a(clipboard_text));
+  else
+    todo_wine_if(todo) ok_(__FILE__,line)(FALSE, "Either 'expected' or 
'clipboard_text' was NULL\n");
+#else
+  todo_wine_if(todo) ok_(__FILE__,line)(!strcmp(expected, clipboard_text), 
"unexpected contents: %s\n", wine_dbgstr_a(clipboard_text));
+#endif
+  GlobalUnlock(global);
+  CloseClipboard();
+}
 
-  hres = ITextRange_GetPara(txtRge, &txtPara);
-  ok(hres == S_OK, "ITextRange_GetPara\n");
-  refcount = get_refcount((IUnknown *)txtPara);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
-  refcount = get_refcount((IUnknown *)txtRge);
-  ok(refcount == 2, "got wrong ref count: %d\n", refcount);
+static void test_clipboard(void)
+{
+  static const char text_in[] = "ab\n c";
+  IRichEditOle *reole = NULL;
+  ITextDocument *doc = NULL;
+  ITextRange *range;
+  ITextSelection *selection;
+  HRESULT hr;
+  HWND hwnd;
 
-  hres = ITextRange_GetPara(txtRge, &txtPara1);
-  ok(hres == S_OK, "ITextRange_GetPara\n");
-  ok(txtPara1 != txtPara, "A new pointer should be return\n");
-  refcount = get_refcount((IUnknown *)txtPara1);
-  ok(refcount == 1, "got wrong ref count: %d\n", refcount);
-  ITextPara_Release(txtPara1);
-  refcount = get_refcount((IUnknown *)txtRge);
-  ok(refcount == 2, "got wrong ref count: %d\n", refcount);
+  create_interfaces(&hwnd, &reole, &doc, &selection);
+  SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text_in);
 
-  ITextRange_Release(txtRge);
-  release_interfaces(&w, &reOle, &txtDoc, NULL);
+  hr = ITextDocument_Range(doc, 0, 0, &range);
+  ok(hr == S_OK, "got 0x%08x\n", hr);
 
-  hres = ITextPara_GetStyle(txtPara, &value);
-  ok(hres == CO_E_RELEASED, "ITextPara after ITextDocument destroyed\n");
+  CLIPBOARD_RANGE_CONTAINS(range, 0, 5, "ab\r\n c")
+  CLIPBOARD_RANGE_CONTAINS(range, 0, 0, "ab\r\n c")
+  CLIPBOARD_RANGE_CONTAINS(range, 1, 1, "ab\r\n c")
+  CLIPBOARD_RANGE_CONTAINS(range, 0, 1, "a")
+  CLIPBOARD_RANGE_CONTAINS(range, 5, 6, "")
+
+  /* Setting password char does not stop Copy */
+  SendMessageA(hwnd, EM_SETPASSWORDCHAR, '*', 0);
+  CLIPBOARD_RANGE_CONTAINS(range, 0, 1, "a")
+
+  /* Cut can be undone */
+  hr = ITextRange_SetRange(range, 0, 1);
+  ok(SUCCEEDED(hr), "SetRange failed: 0x%08x\n", hr);
+  hr = ITextRange_Cut(range, NULL);
+  ok(hr == S_OK, "Cut failed: 0x%08x\n", hr);
+  CLIPBOARD_RANGE_CONTAINS(range, 0, 4, "b\r\n c");
+  hr = ITextDocument_Undo(doc, 1, NULL);
+  todo_wine ok(hr == S_OK, "Undo failed: 0x%08x\n", hr);
+  TODO_CLIPBOARD_RANGE_CONTAINS(range, 0, 5, "ab\r\n c");
+
+  /* Cannot cut when read-only */
+  SendMessageA(hwnd, EM_SETREADONLY, TRUE, 0);
+  hr = ITextRange_SetRange(range, 0, 1);
+  ok(SUCCEEDED(hr), "SetRange failed: 0x%08x\n", hr);
+  hr = ITextRange_Cut(range, NULL);
+  ok(hr == E_ACCESSDENIED, "got 0x%08x\n", hr);
 
-  ITextPara_Release(txtPara);
+  release_interfaces(&hwnd, &reole, &doc, NULL);
+  ITextSelection_Release(selection);
+  ITextRange_Release(range);
 }
 
 START_TEST(richole)
@@ -4088,21 +4263,14 @@ START_TEST(richole)
   test_GetText();
   test_ITextSelection_GetChar();
   test_ITextSelection_GetStart_GetEnd();
-  test_ITextSelection_SetStart();
-  test_ITextSelection_SetEnd();
   test_ITextSelection_Collapse();
-  test_ITextSelection_GetFont();
   test_ITextDocument_Range();
   test_ITextRange_GetChar();
   test_ITextRange_ScrollIntoView();
   test_ITextRange_GetStart_GetEnd();
   test_ITextRange_SetRange();
   test_ITextRange_GetDuplicate();
-  test_ITextRange_SetStart();
-  test_ITextRange_SetEnd();
   test_ITextRange_Collapse();
-  test_ITextRange_GetFont();
-  test_ITextRange_GetPara();
   test_GetClientSite();
   test_IOleWindow_GetWindow();
   test_IOleInPlaceSite_GetWindow();
@@ -4121,5 +4289,7 @@ START_TEST(richole)
   test_GetStoryLength();
   test_ITextSelection_GetDuplicate();
   test_Expand();
-  test_MoveEnd();
+  test_MoveEnd_story();
+  test_character_movement();
+  test_clipboard();
 }
diff --git a/modules/rostests/winetests/riched20/txtsrv.c 
b/modules/rostests/winetests/riched20/txtsrv.c
index dd290e3fb2f..1f789da8a37 100644
--- a/modules/rostests/winetests/riched20/txtsrv.c
+++ b/modules/rostests/winetests/riched20/txtsrv.c
@@ -48,7 +48,7 @@ static PCreateTextServices pCreateTextServices;
 
 /* Use a special table for x86 machines to convert the thiscall
  * calling convention.  This isn't needed on other platforms. */
-#if defined(__i386__) && !defined(__MINGW32__)
+#if  defined(__i386__) && !defined(__MINGW32__) && (!defined(_MSC_VER) || 
!defined(__clang__))
 static ITextServicesVtbl itextServicesStdcallVtbl;
 #define TXTSERV_VTABLE(This) (&itextServicesStdcallVtbl)
 #else /* __i386__ */
@@ -61,8 +61,8 @@ static ITextServicesVtbl itextServicesStdcallVtbl;
 #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_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)
@@ -85,7 +85,10 @@ typedef struct ITextHostTestImpl
 {
     ITextHost ITextHost_iface;
     LONG refCount;
+    HWND window;
+    RECT client_rect;
     CHARFORMAT2W char_format;
+    DWORD scrollbars, props;
 } ITextHostTestImpl;
 
 static inline ITextHostTestImpl *impl_from_ITextHost(ITextHost *iface)
@@ -93,6 +96,11 @@ static inline ITextHostTestImpl 
*impl_from_ITextHost(ITextHost *iface)
     return CONTAINING_RECORD(iface, ITextHostTestImpl, ITextHost_iface);
 }
 
+static const WCHAR lorem[] = L"Lorem ipsum dolor sit amet, consectetur 
adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna 
aliqua. "
+    "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi 
ut aliquip ex ea commodo consequat. "
+    "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum 
dolore eu fugiat nulla pariatur. "
+    "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia 
deserunt mollit anim id est laborum.";
+
 static HRESULT WINAPI ITextHostImpl_QueryInterface(ITextHost *iface,
                                                    REFIID riid,
                                                    LPVOID *ppvObject)
@@ -133,6 +141,7 @@ static HDC __thiscall ITextHostImpl_TxGetDC(ITextHost 
*iface)
 {
     ITextHostTestImpl *This = impl_from_ITextHost(iface);
     TRACECALL("Call to TxGetDC(%p)\n", This);
+    if (This->window) return GetDC( This->window );
     return NULL;
 }
 
@@ -140,6 +149,7 @@ static INT __thiscall ITextHostImpl_TxReleaseDC(ITextHost 
*iface, HDC hdc)
 {
     ITextHostTestImpl *This = impl_from_ITextHost(iface);
     TRACECALL("Call to TxReleaseDC(%p)\n", This);
+    if (This->window) return ReleaseDC( This->window, hdc );
     return 0;
 }
 
@@ -287,7 +297,8 @@ static HRESULT __thiscall 
ITextHostImpl_TxGetClientRect(ITextHost *iface, LPRECT
 {
     ITextHostTestImpl *This = impl_from_ITextHost(iface);
     TRACECALL("Call to TxGetClientRect(%p, prc=%p)\n", This, prc);
-    return E_NOTIMPL;
+    *prc = This->client_rect;
+    return S_OK;
 }
 
 static HRESULT __thiscall ITextHostImpl_TxGetViewInset(ITextHost *iface, 
LPRECT prc)
@@ -333,12 +344,12 @@ static HRESULT __thiscall 
ITextHostImpl_TxGetMaxLength(ITextHost *iface, DWORD *
     return E_NOTIMPL;
 }
 
-static HRESULT __thiscall ITextHostImpl_TxGetScrollBars(ITextHost *iface, 
DWORD *pdwScrollBar)
+static HRESULT __thiscall ITextHostImpl_TxGetScrollBars(ITextHost *iface, 
DWORD *scrollbars)
 {
     ITextHostTestImpl *This = impl_from_ITextHost(iface);
-    TRACECALL("Call to TxGetScrollBars(%p, pdwScrollBar=%p)\n",
-               This, pdwScrollBar);
-    return E_NOTIMPL;
+    TRACECALL("Call to TxGetScrollBars(%p, scrollbars=%p)\n", This, 
scrollbars);
+    *scrollbars = This->scrollbars;
+    return S_OK;
 }
 
 static HRESULT __thiscall ITextHostImpl_TxGetPasswordChar(ITextHost *iface, 
WCHAR *pch)
@@ -383,15 +394,28 @@ static HRESULT __thiscall 
ITextHostImpl_TxGetPropertyBits(ITextHost *iface, DWOR
     ITextHostTestImpl *This = impl_from_ITextHost(iface);
     TRACECALL("Call to TxGetPropertyBits(%p, dwMask=0x%08x, pdwBits=%p)\n",
               This, dwMask, pdwBits);
-    *pdwBits = 0;
+    *pdwBits = This->props & dwMask;
     return S_OK;
 }
 
-static HRESULT __thiscall ITextHostImpl_TxNotify(ITextHost *iface, DWORD 
iNotify, void *pv)
+static int en_vscroll_sent;
+static int en_update_sent;
+static HRESULT __thiscall ITextHostImpl_TxNotify( ITextHost *iface, DWORD 
code, void *data )
 {
     ITextHostTestImpl *This = impl_from_ITextHost(iface);
-    TRACECALL("Call to TxNotify(%p, iNotify=%d, pv=%p)\n", This, iNotify, pv);
-    return E_NOTIMPL;
+    TRACECALL( "Call to TxNotify(%p, code = %#x, data = %p)\n", This, code, 
data );
+    switch (code)
+    {
+    case EN_VSCROLL:
+        en_vscroll_sent++;
+        ok( !data, "got %p\n", data );
+        break;
+    case EN_UPDATE:
+        en_update_sent++;
+        ok( !data, "got %p\n", data );
+        break;
+    }
+    return S_OK;
 }
 
 static HIMC __thiscall ITextHostImpl_TxImmGetContext(ITextHost *iface)
@@ -492,7 +516,7 @@ typedef struct
 
 static void setup_thiscall_wrappers(void)
 {
-#if defined(__i386__) && !defined(__MINGW32__)
+#if defined(__i386__) && !defined(__MINGW32__) && (!defined(_MSC_VER) || 
!defined(__clang__))
     void** pVtable;
     void** pVtableEnd;
     THISCALL_TO_STDCALL_THUNK *thunk;
@@ -591,9 +615,13 @@ static BOOL init_texthost(ITextServices **txtserv, 
ITextHost **ret)
     }
     dummyTextHost->ITextHost_iface.lpVtbl = &itextHostVtbl;
     dummyTextHost->refCount = 1;
+    dummyTextHost->window = NULL;
+    SetRectEmpty( &dummyTextHost->client_rect );
     memset(&dummyTextHost->char_format, 0, sizeof(dummyTextHost->char_format));
     dummyTextHost->char_format.cbSize = sizeof(dummyTextHost->char_format);
     dummyTextHost->char_format.dwMask = CFM_ALL2;
+    dummyTextHost->scrollbars = 0;
+    dummyTextHost->props = 0;
     hf = GetStockObject(DEFAULT_GUI_FONT);
     hf_to_cf(hf, &dummyTextHost->char_format);
 
@@ -602,6 +630,7 @@ static BOOL init_texthost(ITextServices **txtserv, 
ITextHost **ret)
        ITextServices object. */
     result = pCreateTextServices(NULL, &dummyTextHost->ITextHost_iface, &init);
     ok(result == S_OK, "Did not return S_OK when created (result =  %x)\n", 
result);
+    ok(dummyTextHost->refCount == 1, "host ref %d\n", dummyTextHost->refCount);
     if (result != S_OK) {
         CoTaskMemFree(dummyTextHost);
         win_skip("CreateTextServices failed.\n");
@@ -621,9 +650,30 @@ static BOOL init_texthost(ITextServices **txtserv, 
ITextHost **ret)
     return TRUE;
 }
 
+static void fill_reobject_struct(REOBJECT *reobj, LONG cp, LPOLEOBJECT poleobj,
+        LPSTORAGE pstg, LPOLECLIENTSITE polesite, LONG sizel_cx,
+        LONG sizel_cy, DWORD aspect, DWORD flags, DWORD user)
+{
+    reobj->cbStruct = sizeof(*reobj);
+    reobj->clsid = CLSID_NULL;
+    reobj->cp = cp;
+    reobj->poleobj = poleobj;
+    reobj->pstg = pstg;
+    reobj->polesite = polesite;
+    reobj->sizel.cx = sizel_cx;
+    reobj->sizel.cy = sizel_cy;
+    reobj->dvaspect = aspect;
+    reobj->dwFlags = flags;
+    reobj->dwUser = user;
+}
+
 static void test_TxGetText(void)
 {
+    const WCHAR *expected_string;
+    IOleClientSite *clientsite;
     ITextServices *txtserv;
+    IRichEditOle *reole;
+    REOBJECT reobject;
     ITextHost *host;
     HRESULT hres;
     BSTR rettext;
@@ -635,6 +685,24 @@ static void test_TxGetText(void)
     ok(hres == S_OK, "ITextServices_TxGetText failed (result = %x)\n", hres);
     SysFreeString(rettext);
 
+    hres = ITextServices_TxSetText(txtserv, L"abcdefg");
+    ok(hres == S_OK, "Got hres: %#x.\n", hres);
+    hres = ITextServices_QueryInterface(txtserv, &IID_IRichEditOle, (void 
**)&reole);
+    ok(hres == S_OK, "Got hres: %#x.\n", hres);
+    hres = IRichEditOle_GetClientSite(reole, &clientsite);
+    ok(hres == S_OK, "Got hres: %#x.\n", hres);
+    expected_string = L"abc\xfffc""defg";
+    fill_reobject_struct(&reobject, 3, NULL, NULL, clientsite, 10, 10, 
DVASPECT_CONTENT, 0, 1);
+    hres = IRichEditOle_InsertObject(reole, &reobject);
+    ok(hres == S_OK, "Got hres: %#x.\n", hres);
+    hres = ITextServices_TxGetText(txtserv, &rettext);
+    ok(hres == S_OK, "Got hres: %#x.\n", hres);
+    ok(lstrlenW(rettext) == lstrlenW(expected_string), "Got wrong length: 
%d.\n", lstrlenW(rettext));
+    todo_wine ok(!lstrcmpW(rettext, expected_string), "Got wrong content: 
%s.\n", debugstr_w(rettext));
+    SysFreeString(rettext);
+    IOleClientSite_Release(clientsite);
+    IRichEditOle_Release(reole);
+
     ITextServices_Release(txtserv);
     ITextHost_Release(host);
 }
@@ -689,7 +757,7 @@ static void _check_txgetnaturalsize(HRESULT res, LONG 
width, LONG height, HDC hd
     expected_width = expected_rect.right - expected_rect.left;
     expected_height = expected_rect.bottom - expected_rect.top;
     ok_(__FILE__,line)(res == S_OK, "ITextServices_TxGetNaturalSize failed: 
0x%08x.\n", res);
-    ok_(__FILE__,line)(width >= expected_width && width <= expected_width + 1,
+    todo_wine ok_(__FILE__,line)(width >= expected_width && width <= 
expected_width + 1,
                        "got wrong width: %d, expected: %d {+1}.\n", width, 
expected_width);
     ok_(__FILE__,line)(height == expected_height, "got wrong height: %d, 
expected: %d.\n",
                        height, expected_height);
@@ -701,7 +769,7 @@ static void test_TxGetNaturalSize(void)
     ITextHost *host;
     HRESULT result;
     SIZEL extent;
-    static const WCHAR test_text[] = 
{'T','e','s','t','S','o','m','e','T','e','x','t',0};
+    static const WCHAR test_text[] = L"TestSomeText";
     LONG width, height;
     HDC hdcDraw;
     HWND hwnd;
@@ -736,7 +804,7 @@ static void test_TxGetNaturalSize(void)
     height = 0;
     result = ITextServices_TxGetNaturalSize(txtserv, DVASPECT_CONTENT, 
hdcDraw, NULL, NULL,
                                             TXTNS_FITTOCONTENT, &extent, 
&width, &height);
-    todo_wine CHECK_TXGETNATURALSIZE(result, width, height, hdcDraw, rect, 
test_text);
+    CHECK_TXGETNATURALSIZE(result, width, height, hdcDraw, rect, test_text);
 
     ReleaseDC(hwnd, hdcDraw);
     DestroyWindow(hwnd);
@@ -748,26 +816,51 @@ static void test_TxDraw(void)
 {
     ITextServices *txtserv;
     ITextHost *host;
-    HDC tmphdc = GetDC(NULL);
-    DWORD dwAspect = DVASPECT_CONTENT;
-    HDC hicTargetDev = NULL; /* Means "default" device */
-    DVTARGETDEVICE *ptd = NULL;
-    void *pvAspect = NULL;
-    HRESULT result;
-    RECTL client = {0,0,100,100};
-
+    HRESULT hr;
+    RECT client = {0, 0, 100, 100};
+    ITextHostTestImpl *host_impl;
+    HDC hdc;
 
     if (!init_texthost(&txtserv, &host))
         return;
 
-    todo_wine {
-        result = ITextServices_TxDraw(txtserv, dwAspect, 0, pvAspect, ptd,
-                                      tmphdc, hicTargetDev, &client, NULL,
-                                      NULL, NULL, 0, 0);
-        ok(result == S_OK, "TxDraw failed (result = %x)\n", result);
-    }
-
+    host_impl = impl_from_ITextHost( host );
+    host_impl->window = CreateWindowExA( 0, "static", NULL, WS_POPUP | 
WS_VISIBLE,
+                                         0, 0, 400, 400, 0, 0, 0, NULL );
+    host_impl->client_rect = client;
+    host_impl->props = TXTBIT_MULTILINE | TXTBIT_RICHTEXT | TXTBIT_WORDWRAP;
+    ITextServices_OnTxPropertyBitsChange( txtserv, TXTBIT_CLIENTRECTCHANGE | 
TXTBIT_MULTILINE | TXTBIT_RICHTEXT | TXTBIT_WORDWRAP,
+                                          host_impl->props );
+    hdc = GetDC( host_impl->window );
+
+    hr = ITextServices_TxDraw( txtserv, DVASPECT_CONTENT, 0, NULL, NULL, NULL, 
NULL, NULL, NULL,
+                               NULL, NULL, 0, TXTVIEW_INACTIVE );
+    ok( hr == E_INVALIDARG, "got %08x\n", hr );
+    hr = ITextServices_TxDraw( txtserv, DVASPECT_CONTENT, 0, NULL, NULL, hdc, 
NULL, NULL, NULL,
+                               NULL, NULL, 0, TXTVIEW_INACTIVE );
+    ok( hr == E_INVALIDARG, "got %08x\n", hr );
+    hr = ITextServices_TxDraw( txtserv, DVASPECT_CONTENT, 0, NULL, NULL, NULL, 
NULL, (RECTL *)&client, NULL,
+                               NULL, NULL, 0, TXTVIEW_INACTIVE );
+    ok( hr == E_FAIL, "got %08x\n", hr );
+    hr = ITextServices_TxDraw( txtserv, DVASPECT_CONTENT, 0, NULL, NULL, hdc, 
NULL, (RECTL *)&client, NULL,
+                               NULL, NULL, 0, TXTVIEW_INACTIVE );
+    ok( hr == S_OK, "got %08x\n", hr );
+    hr = ITextServices_TxDraw( txtserv, DVASPECT_CONTENT, 0, NULL, NULL, hdc, 
NULL, (RECTL *)&client, NULL,
+                               NULL, NULL, 0, TXTVIEW_ACTIVE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = ITextServices_OnTxInPlaceActivate( txtserv, &client );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = ITextServices_TxDraw( txtserv, DVASPECT_CONTENT, 0, NULL, NULL, NULL, 
NULL, NULL, NULL,
+                               NULL, NULL, 0, TXTVIEW_INACTIVE );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    hr = ITextServices_OnTxInPlaceDeactivate( txtserv );
+
+    ReleaseDC( host_impl->window, hdc );
     ITextServices_Release(txtserv);
+    DestroyWindow( host_impl->window );
     ITextHost_Release(host);
 }
 
@@ -863,6 +956,7 @@ static void test_QueryInterface(void)
     IRichEditOle *reole, *txtsrv_reole;
     ITextDocument *txtdoc, *txtsrv_txtdoc;
     ITextDocument2Old *txtdoc2old, *txtsrv_txtdoc2old;
+    IUnknown *unk, *unk2;
     ULONG refcount;
 
     if(!init_texthost(&txtserv, &host))
@@ -879,6 +973,14 @@ static void test_QueryInterface(void)
     refcount = get_refcount((IUnknown *)txtsrv_reole);
     ok(refcount == 2, "got wrong ref count: %d\n", refcount);
 
+    hres = ITextServices_QueryInterface( txtserv, &IID_IUnknown, (void **)&unk 
);
+    ok( hres == S_OK, "got 0x%08x\n", hres );
+    hres = IRichEditOle_QueryInterface( txtsrv_reole, &IID_IUnknown, (void 
**)&unk2 );
+    ok( hres == S_OK, "got 0x%08x\n", hres );
+    ok( unk == unk2, "unknowns differ\n" );
+    IUnknown_Release( unk2 );
+    IUnknown_Release( unk );
+
     hres = IRichEditOle_QueryInterface(txtsrv_reole, &IID_ITextDocument, (void 
**)&txtdoc);
     ok(hres == S_OK, "IRichEditOle_QueryInterface: 0x%08x\n", hres);
     refcount = get_refcount((IUnknown *)txtserv);
@@ -987,20 +1089,134 @@ static void test_TxGetScroll(void)
     ITextServices *txtserv;
     ITextHost *host;
     HRESULT ret;
+    LONG min_pos, max_pos, pos, page;
+    BOOL enabled;
+    ITextHostTestImpl *host_impl;
+    RECT client = {0, 0, 100, 100};
 
     if (!init_texthost(&txtserv, &host))
         return;
 
+    host_impl = impl_from_ITextHost( host );
+
     ret = ITextServices_TxGetHScroll(txtserv, NULL, NULL, NULL, NULL, NULL);
-    ok(ret == S_OK, "ITextSerHices_GetVScroll failed: 0x%08x.\n", ret);
+    ok(ret == S_OK, "ITextServices_TxGetHScroll failed: 0x%08x.\n", ret);
 
     ret = ITextServices_TxGetVScroll(txtserv, NULL, NULL, NULL, NULL, NULL);
-    ok(ret == S_OK, "ITextServices_GetVScroll failed: 0x%08x.\n", ret);
-
+    ok(ret == S_OK, "ITextServices_TxGetVScroll failed: 0x%08x.\n", ret);
+
+    ret = ITextServices_TxGetVScroll( txtserv, &min_pos, &max_pos, &pos, 
&page, &enabled );
+    ok( ret == S_OK, "ITextServices_TxGetHScroll failed: 0x%08x.\n", ret );
+    ok( min_pos == 0, "got %d\n", min_pos );
+    ok( max_pos == 0, "got %d\n", max_pos );
+    ok( pos == 0, "got %d\n", pos );
+    ok( page == 0, "got %d\n", page );
+    ok( !enabled, "got %d\n", enabled );
+
+    host_impl->scrollbars = WS_VSCROLL;
+    host_impl->props = TXTBIT_MULTILINE | TXTBIT_RICHTEXT | TXTBIT_WORDWRAP;
+    ITextServices_OnTxPropertyBitsChange( txtserv, TXTBIT_SCROLLBARCHANGE | 
TXTBIT_MULTILINE | TXTBIT_RICHTEXT | TXTBIT_WORDWRAP, host_impl->props );
+
+    host_impl->window = CreateWindowExA( 0, "static", NULL, WS_POPUP | 
WS_VISIBLE,
+                                         0, 0, 400, 400, 0, 0, 0, NULL );
+    host_impl->client_rect = client;
+    ret = ITextServices_OnTxInPlaceActivate( txtserv, &client );
+    ok( ret == S_OK, "got 0x%08x.\n", ret );
+
+    ret = ITextServices_TxGetVScroll( txtserv, &min_pos, &max_pos, &pos, 
&page, &enabled );
+    ok( ret == S_OK, "ITextServices_TxGetHScroll failed: 0x%08x.\n", ret );
+    ok( min_pos == 0, "got %d\n", min_pos );
+todo_wine
+    ok( max_pos == 0, "got %d\n", max_pos );
+    ok( pos == 0, "got %d\n", pos );
+    ok( page == client.bottom, "got %d\n", page );
+    ok( !enabled, "got %d\n", enabled );
+
+    ret = ITextServices_TxSetText( txtserv, lorem );
+    ok( ret == S_OK, "got 0x%08x.\n", ret );
+
+    ret = ITextServices_TxGetVScroll( txtserv, &min_pos, &max_pos, &pos, 
&page, &enabled );
+    ok( ret == S_OK, "ITextServices_TxGetHScroll failed: 0x%08x.\n", ret );
+    ok( min_pos == 0, "got %d\n", min_pos );
+    ok( max_pos > client.bottom, "got %d\n", max_pos );
+    ok( pos == 0, "got %d\n", pos );
+    ok( page == client.bottom, "got %d\n", page );
+    ok( enabled, "got %d\n", enabled );
+
+    host_impl->scrollbars = WS_VSCROLL | ES_DISABLENOSCROLL;
+    ITextServices_OnTxPropertyBitsChange( txtserv, TXTBIT_SCROLLBARCHANGE, 
host_impl->props );
+    ITextServices_TxSetText( txtserv, L"short" );
+
+    ret = ITextServices_TxGetVScroll( txtserv, &min_pos, &max_pos, &pos, 
&page, &enabled );
+    ok( ret == S_OK, "ITextServices_TxGetHScroll failed: 0x%08x.\n", ret );
+    ok( min_pos == 0, "got %d\n", min_pos );
+todo_wine
+    ok( max_pos == 0, "got %d\n", max_pos );
+    ok( pos == 0, "got %d\n", pos );
+    ok( page == client.bottom, "got %d\n", page );
+    ok( !enabled, "got %d\n", enabled );
+
+    DestroyWindow( host_impl->window );
     ITextServices_Release(txtserv);
     ITextHost_Release(host);
 }
 
+static void test_notifications( void )
+{
+    ITextServices *txtserv;
+    ITextHost *host;
+    LRESULT res;
+    HRESULT hr;
+    RECT client = { 0, 0, 100, 100 };
+    ITextHostTestImpl *host_impl;
+
+    init_texthost( &txtserv, &host );
+    host_impl = impl_from_ITextHost( host );
+
+    host_impl->scrollbars = WS_VSCROLL;
+    host_impl->props = TXTBIT_MULTILINE | TXTBIT_RICHTEXT | TXTBIT_WORDWRAP;
+    ITextServices_OnTxPropertyBitsChange( txtserv, TXTBIT_SCROLLBARCHANGE | 
TXTBIT_MULTILINE | TXTBIT_RICHTEXT | TXTBIT_WORDWRAP, host_impl->props );
+
+    ITextServices_TxSetText( txtserv, lorem );
+
+    host_impl->window = CreateWindowExA( 0, "static", NULL, WS_POPUP | 
WS_VISIBLE,
+                                         0, 0, 400, 400, 0, 0, 0, NULL );
+    host_impl->client_rect = client;
+    hr = ITextServices_OnTxInPlaceActivate( txtserv, &client );
+    ok( hr == S_OK, "got 0x%08x.\n", hr );
+
+    hr = ITextServices_TxSendMessage( txtserv, EM_SETEVENTMASK, 0, ENM_SCROLL, 
&res );
+    ok( hr == S_OK, "got %08x\n", hr );
+
+    /* check EN_VSCROLL notification is sent */
+    en_vscroll_sent = 0;
+    hr = ITextServices_TxSendMessage( txtserv, WM_VSCROLL, SB_LINEDOWN, 0, 
&res );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( en_vscroll_sent == 1, "got %d\n", en_vscroll_sent );
+
+    hr = ITextServices_TxSendMessage( txtserv, WM_VSCROLL, SB_BOTTOM, 0, &res 
);
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( en_vscroll_sent == 2, "got %d\n", en_vscroll_sent );
+
+    /* but not when the thumb is moved */
+    hr = ITextServices_TxSendMessage( txtserv, WM_VSCROLL, MAKEWPARAM( 
SB_THUMBTRACK, 0 ), 0, &res );
+    ok( hr == S_OK, "got %08x\n", hr );
+    hr = ITextServices_TxSendMessage( txtserv, WM_VSCROLL, MAKEWPARAM( 
SB_THUMBPOSITION, 0 ), 0, &res );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( en_vscroll_sent == 2, "got %d\n", en_vscroll_sent );
+
+    /* EN_UPDATE is sent by TxDraw() */
+    en_update_sent = 0;
+    hr = ITextServices_TxDraw( txtserv, DVASPECT_CONTENT, 0, NULL, NULL, NULL, 
NULL, NULL, NULL,
+                               NULL, NULL, 0, TXTVIEW_ACTIVE );
+    ok( hr == S_OK, "got %08x\n", hr );
+    ok( en_update_sent == 1, "got %d\n", en_update_sent );
+
+    DestroyWindow( host_impl->window );
+    ITextServices_Release( txtserv );
+    ITextHost_Release( host );
+}
+
 START_TEST( txtsrv )
 {
     ITextServices *txtserv;
@@ -1033,6 +1249,7 @@ START_TEST( txtsrv )
         test_QueryInterface();
         test_default_format();
         test_TxGetScroll();
+        test_notifications();
     }
     if (wrapperCodeMem) VirtualFree(wrapperCodeMem, 0, MEM_RELEASE);
 }
diff --git a/sdk/include/psdk/textserv.h b/sdk/include/psdk/textserv.h
index 455e88c5581..26278cc85c8 100644
--- a/sdk/include/psdk/textserv.h
+++ b/sdk/include/psdk/textserv.h
@@ -33,9 +33,9 @@ extern "C" {
 #define THISCALLMETHOD_(type,method)  type (__thiscall *method)
 #endif
 
-DEFINE_GUID(IID_ITextServices,0x8d33f740,0xcf58,0x11ce,0xa8,0x9d,0x00,0xaa,0x00,0x6c,0xad,0xc5);
-DEFINE_GUID(IID_ITextHost,    
0xc5bdd8d0,0xd26e,0x11ce,0xa8,0x9e,0x00,0xaa,0x00,0x6c,0xad,0xc5);
-DEFINE_GUID(IID_ITextHost2,   
0xc5bdd8d0,0xd26e,0x11ce,0xa8,0x9e,0x00,0xaa,0x00,0x6c,0xad,0xc5);
+EXTERN_C const IID IID_ITextServices;
+EXTERN_C const IID IID_ITextHost;
+EXTERN_C const IID IID_ITextHost2;
 
 /*****************************************************************************
  * ITextServices interface
@@ -108,10 +108,10 @@ DECLARE_INTERFACE_(ITextServices,IUnknown)
         INT y,
         DWORD* pHitResult) PURE;
 
-    THISCALLMETHOD_(HRESULT,OnTxInplaceActivate)( THIS_
+    THISCALLMETHOD_(HRESULT,OnTxInPlaceActivate)( THIS_
         LPCRECT prcClient) PURE;
 
-    THISCALLMETHOD_(HRESULT,OnTxInplaceDeactivate)( THIS ) PURE;
+    THISCALLMETHOD_(HRESULT,OnTxInPlaceDeactivate)( THIS ) PURE;
 
     THISCALLMETHOD_(HRESULT,OnTxUIActivate)( THIS ) PURE;
 
@@ -151,6 +151,7 @@ DECLARE_INTERFACE_(ITextServices,IUnknown)
         DWORD* pdwHeight) PURE;
 
 };
+#undef INTERFACE
 
 #ifdef COBJMACROS
 /*** IUnknown methods ***/
@@ -159,8 +160,6 @@ DECLARE_INTERFACE_(ITextServices,IUnknown)
 #define ITextServices_Release(p) (p)->lpVtbl->Release(p)
 #endif
 
-#undef INTERFACE
-
 typedef enum _TXTBACKSTYLE {
     TXTBACK_TRANSPARENT = 0,
     TXTBACK_OPAQUE
@@ -180,7 +179,7 @@ enum TXTNATURALSIZE {
 
 enum TXTVIEW {
     TXTVIEW_ACTIVE = 0,
-    TXTVIEW_INACTIVE = 1
+    TXTVIEW_INACTIVE = -1
 };
 
 #define TXTBIT_RICHTEXT         0x000001
@@ -361,6 +360,7 @@ DECLARE_INTERFACE_(ITextHost,IUnknown)
         LONG* lSelBarWidth) PURE;
 
 };
+#undef INTERFACE
 
 #ifdef COBJMACROS
 /*** IUnknown methods ***/
@@ -369,8 +369,80 @@ DECLARE_INTERFACE_(ITextHost,IUnknown)
 #define ITextHost_Release(p) (p)->lpVtbl->Release(p)
 #endif
 
+/*****************************************************************************
+ * ITextHost2 interface
+ */
+#define INTERFACE ITextHost2
+DECLARE_INTERFACE_(ITextHost2,ITextHost)
+{
+    /*** IUnknown methods ***/
+    STDMETHOD(QueryInterface)( THIS_ REFIID riid, void** ppvObject ) PURE;
+    STDMETHOD_(ULONG,AddRef)( THIS ) PURE;
+    STDMETHOD_(ULONG,Release)( THIS ) PURE;
+    /*** ITextHost methods ***/
+    THISCALLMETHOD_(HDC,TxGetDC)( THIS ) PURE;
+    THISCALLMETHOD_(INT,TxReleaseDC)( THIS_ HDC hdc ) PURE;
+    THISCALLMETHOD_(BOOL,TxShowScrollBar)( THIS_ INT fnBar, BOOL fShow ) PURE;
+    THISCALLMETHOD_(BOOL,TxEnableScrollBar)( THIS_ INT fuSBFlags, INT 
fuArrowflags ) PURE;
+    THISCALLMETHOD_(BOOL,TxSetScrollRange)( THIS_ INT fnBar, LONG nMinPos, INT 
nMaxPos, BOOL fRedraw ) PURE;
+    THISCALLMETHOD_(BOOL,TxSetScrollPos)( THIS_ INT fnBar, INT nPos, BOOL 
fRedraw ) PURE;
+    THISCALLMETHOD_(void,TxInvalidateRect)( THIS_ LPCRECT prc, BOOL fMode ) 
PURE;
+    THISCALLMETHOD_(void,TxViewChange)( THIS_ BOOL fUpdate ) PURE;
+    THISCALLMETHOD_(BOOL,TxCreateCaret)( THIS_ HBITMAP hbmp, INT xWidth, INT 
yHeight ) PURE;
+    THISCALLMETHOD_(BOOL,TxShowCaret)( THIS_ BOOL fShow ) PURE;
+    THISCALLMETHOD_(BOOL,TxSetCaretPos)( THIS_ INT x, INT y ) PURE;
+    THISCALLMETHOD_(BOOL,TxSetTimer)( THIS_ UINT idTimer, UINT uTimeout ) PURE;
+    THISCALLMETHOD_(void,TxKillTimer)( THIS_ UINT idTimer ) PURE;
+    THISCALLMETHOD_(void,TxScrollWindowEx)( THIS_ INT dx, INT dy, LPCRECT 
lprcScroll, LPCRECT lprcClip,
+                                            HRGN hRgnUpdate, LPRECT 
lprcUpdate, UINT fuScroll ) PURE;
+    THISCALLMETHOD_(void,TxSetCapture)( THIS_ BOOL fCapture ) PURE;
+    THISCALLMETHOD_(void,TxSetFocus)( THIS ) PURE;
+    THISCALLMETHOD_(void,TxSetCursor)( THIS_ HCURSOR hcur, BOOL fText ) PURE;
+    THISCALLMETHOD_(BOOL,TxScreenToClient)( THIS_ LPPOINT lppt ) PURE;
+    THISCALLMETHOD_(BOOL,TxClientToScreen)( THIS_ LPPOINT lppt ) PURE;
+    THISCALLMETHOD_(HRESULT,TxActivate)( THIS_ LONG* plOldState ) PURE;
+    THISCALLMETHOD_(HRESULT,TxDeactivate)( THIS_ LONG lNewState ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetClientRect)( THIS_ LPRECT prc ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetViewInset)( THIS_ LPRECT prc ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetCharFormat)( THIS_ const CHARFORMATW** ppCF ) 
PURE;
+    THISCALLMETHOD_(HRESULT,TxGetParaFormat)( THIS_ const PARAFORMAT** ppPF ) 
PURE;
+    THISCALLMETHOD_(COLORREF,TxGetSysColor)( THIS_ int nIndex ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetBackStyle)( THIS_ TXTBACKSTYLE* pStyle ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetMaxLength)( THIS_ DWORD* plength ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetScrollBars)( THIS_ DWORD* pdwScrollBar ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetPasswordChar)( THIS_ WCHAR* pch ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetAcceleratorPos)( THIS_ LONG* pch ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetExtent)( THIS_ LPSIZEL lpExtent ) PURE;
+    THISCALLMETHOD_(HRESULT,OnTxCharFormatChange)( THIS_ const CHARFORMATW* 
pcf ) PURE;
+    THISCALLMETHOD_(HRESULT,OnTxParaFormatChange)( THIS_ const PARAFORMAT* ppf 
) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetPropertyBits)( THIS_ DWORD dwMask, DWORD* 
pdwBits ) PURE;
+    THISCALLMETHOD_(HRESULT,TxNotify)( THIS_ DWORD iNotify, void* pv ) PURE;
+    THISCALLMETHOD_(HIMC,TxImmGetContext)( THIS ) PURE;
+    THISCALLMETHOD_(void,TxImmReleaseContext)( THIS_ HIMC himc ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetSelectionBarWidth)( THIS_ LONG* lSelBarWidth 
) PURE;
+    /* ITextHost2 methods */
+    THISCALLMETHOD_(BOOL,TxIsDoubleClickPending)( THIS ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetWindow)( THIS_ HWND *hwnd ) PURE;
+    THISCALLMETHOD_(HRESULT,TxSetForegroundWindow)( THIS ) PURE;
+    THISCALLMETHOD_(HPALETTE,TxGetPalette)( THIS ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetEastAsianFlags)( THIS_ LONG *flags ) PURE;
+    THISCALLMETHOD_(HCURSOR,TxSetCursor2)( THIS_ HCURSOR cursor, BOOL text ) 
PURE;
+    THISCALLMETHOD_(void,TxFreeTextServicesNotification)( THIS ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetEditStyle)( THIS_ DWORD item, DWORD *data ) 
PURE;
+    THISCALLMETHOD_(HRESULT,TxGetWindowStyles)( THIS_ DWORD *style, DWORD 
*ex_style ) PURE;
+    THISCALLMETHOD_(HRESULT,TxShowDropCaret)( THIS_ BOOL show, HDC hdc, const 
RECT *rect ) PURE;
+    THISCALLMETHOD_(HRESULT,TxDestroyCaret)( THIS ) PURE;
+    THISCALLMETHOD_(HRESULT,TxGetHorzExtent)( THIS_ LONG *horz_extent ) PURE;
+};
 #undef INTERFACE
 
+#ifdef COBJMACROS
+/*** IUnknown methods ***/
+#define ITextHost2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define ITextHost2_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define ITextHost2_Release(p) (p)->lpVtbl->Release(p)
+#endif
+
 HRESULT WINAPI CreateTextServices(IUnknown*,ITextHost*,IUnknown**);
 
 typedef HRESULT (WINAPI *PCreateTextServices)(IUnknown*,ITextHost*,IUnknown**);

Reply via email to