Author: pschweitzer
Date: Tue Jul  8 05:09:42 2008
New Revision: 34368
URL: 
http://svn.reactos.org/svn/reactos?rev=34368&view=rev
Log:
Synced riched20_winetest.exe with Wine HEAD
Added:
    trunk/rostests/winetests/riched20/richole.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] Tue Jul  8 05:09:42 2008
@@ -47,6 +47,40 @@
   return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
 }
+static void processPendingMessages(void)
+{
+    MSG msg;
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+}
+
+static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
+{
+    BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
+    BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
+    SetFocus(hwnd);
+    keybd_event(mod_vk, mod_scan_code, 0, 0);
+    keybd_event(vk, scan_code, 0, 0);
+    keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
+    keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
+    processPendingMessages();
+}
+
+static void simulate_typing_characters(HWND hwnd, const char* szChars)
+{
+    int ret;
+
+    while (*szChars != '\0') {
+        SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
+        ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
+        ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
+        SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
+        szChars++;
+    }
+}
+
 static const char haystack[] = "WINEWine wineWine wine WineWine";
                              /* ^0        ^10       ^20       ^30 */
@@ -141,14 +175,16 @@
   ft.lpstrText = f->needle;
   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
   ok(findloc == f->expected_loc,
-     "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at
%d\n",
-     name, id, f->needle, f->start, f->end, f->flags, findloc);
+     "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d,
expected %d\n",
+     name, id, f->needle, f->start, f->end, f->flags, findloc,
f->expected_loc);
 }
 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
     int id) {
   int findloc;
   FINDTEXTEX ft;
+  int expected_end_loc;
+
   memset(&ft, 0, sizeof(ft));
   ft.chrg.cpMin = f->start;
   ft.chrg.cpMax = f->end;
@@ -160,10 +196,11 @@
   ok(ft.chrgText.cpMin == f->expected_loc,
       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at
%d\n",
       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
-  ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
-        : f->expected_loc + strlen(f->needle)),
-      "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at
%d\n",
-      name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
+  expected_end_loc = ((f->expected_loc == -1) ? -1
+        : f->expected_loc + strlen(f->needle));
+  ok(ft.chrgText.cpMax == expected_end_loc,
+      "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d,
expected %d\n",
+      name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax,
expected_end_loc);
 }
 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
@@ -187,6 +224,7 @@
 static void test_EM_FINDTEXT(void)
 {
   HWND hwndRichEdit = new_richedit(NULL);
+  CHARFORMAT2 cf2;
   /* Empty rich edit control */
   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
@@ -196,6 +234,30 @@
   /* Haystack text */
   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
+      sizeof(find_tests2)/sizeof(struct find_s));
+
+  /* Setting a format on an arbitrary range should have no effect in search
+     results. This tests correct offset reporting across runs. */
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
+             (LPARAM) &cf2);
+  cf2.dwMask = CFM_ITALIC | cf2.dwMask;
+  cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
+  SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
+  SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  /* Haystack text, again */
+  run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
+      sizeof(find_tests2)/sizeof(struct find_s));
+
+  /* Yet another range */
+  cf2.dwMask = CFM_BOLD | cf2.dwMask;
+  cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
+  SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
+  SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  /* Haystack text, again */
+  run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
       sizeof(find_tests2)/sizeof(struct find_s));
   DestroyWindow(hwndRichEdit);
@@ -369,11 +431,141 @@
   DestroyWindow(hwndRichEdit);
 }
+static void test_EM_POSFROMCHAR(void)
+{
+  HWND hwndRichEdit = new_richedit(NULL);
+  int i;
+  LRESULT result;
+  unsigned int height = 0;
+  int xpos = 0;
+  static const char text[] = "aa\n"
+      "this is a long line of text that should be longer than the "
+      "control's width\n"
+      "cc\n"
+      "dd\n"
+      "ee\n"
+      "ff\n"
+      "gg\n"
+      "hh\n";
+
+  /* Fill the control to lines to ensure that most of them are offscreen */
+  for (i = 0; i < 50; i++)
+  {
+    /* Do not modify the string; it is exactly 16 characters long. */
+    SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
+    SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
+  }
+
+  /*
+   Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns
void.
+   Richedit 2.0 receives character offset on wParam, ignores lParam, returns
MAKELONG(x,y)
+   Richedit 3.0 accepts either of the above API conventions.
+   */
+
+  /* Testing Richedit 2.0 API format */
+
+  /* Testing start of lines. X-offset should be constant on all cases (native is 1).
+     Since all lines are identical and drawn with the same font,
+     they should have the same height... right?
+   */
+  for (i = 0; i < 50; i++)
+  {
+    /* All the lines are 16 characters long */
+    result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
+    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)
+    {
+      ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected >
0\n", HIWORD(result));
+      ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n",
LOWORD(result));
+      height = HIWORD(result);
+    }
+    else
+    {
+      ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected
%d\n", HIWORD(result), i * height);
+      ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n",
LOWORD(result));
+    }
+  }
+
+  /* Testing position at end of text */
+  result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
+  ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected
%d\n", HIWORD(result), 50 * height);
+  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n",
LOWORD(result));
+
+  /* Testing position way past end of text */
+  result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
+  ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected
%d\n", HIWORD(result), 50 * height);
+  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n",
LOWORD(result));
+
+  /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
+  SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
+  for (i = 0; i < 50; i++)
+  {
+    /* All the lines are 16 characters long */
+    result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
+    ok((signed short)(HIWORD(result)) == (i - 1) * height,
+        "EM_POSFROMCHAR reports y=%hd, expected %d\n",
+        (signed short)(HIWORD(result)), (i - 1) * height);
+    ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n",
LOWORD(result));
+  }
+
+  /* Testing position at end of text */
+  result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
+  ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected
%d\n", HIWORD(result), (50 - 1) * height);
+  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n",
LOWORD(result));
+
+  /* Testing position way past end of text */
+  result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
+  ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected
%d\n", HIWORD(result), (50 - 1) * height);
+  ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n",
LOWORD(result));
+
+  /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
+  SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
+
+  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);
+  result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
+  ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n",
HIWORD(result));
+  todo_wine {
+  /* Fails on builtin because horizontal scrollbar is not being shown */
+  ok((signed short)(LOWORD(result)) < xpos,
+        "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
+        (signed short)(LOWORD(result)), xpos);
+  }
+  DestroyWindow(hwndRichEdit);
+}
+
 static void test_EM_SETCHARFORMAT(void)
 {
   HWND hwndRichEdit = new_richedit(NULL);
   CHARFORMAT2 cf2;
   int rc = 0;
+  int tested_effects[] = {
+    CFE_BOLD,
+    CFE_ITALIC,
+    CFE_UNDERLINE,
+    CFE_STRIKEOUT,
+    CFE_PROTECTED,
+    CFE_LINK,
+    CFE_SUBSCRIPT,
+    CFE_SUPERSCRIPT,
+    0
+  };
+  int i;
+  CHARRANGE cr;
   /* Invalid flags, CHARFORMAT2 structure blanked out */
   memset(&cf2, 0, sizeof(cf2));
@@ -411,6 +603,9 @@
   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
              (LPARAM) &cf2);
   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
+  rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
+  ok(rc == FALSE, "Should not be able to undo here.\n");
+  SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
   /* A valid flag, CHARFORMAT2 structure minimally filled */
   memset(&cf2, 0, sizeof(cf2));
@@ -418,6 +613,9 @@
   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
              (LPARAM) &cf2);
   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
+  rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
+  todo_wine ok(rc == FALSE, "Should not be able to undo here.\n");
+  SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
   /* A valid flag, CHARFORMAT2 structure minimally filled */
   memset(&cf2, 0, sizeof(cf2));
@@ -425,6 +623,9 @@
   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
              (LPARAM) &cf2);
   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
+  rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
+  ok(rc == FALSE, "Should not be able to undo here.\n");
+  SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
   /* A valid flag, CHARFORMAT2 structure minimally filled */
   memset(&cf2, 0, sizeof(cf2));
@@ -432,6 +633,9 @@
   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
              (LPARAM) &cf2);
   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
+  rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
+  todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
+  SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
   /* A valid flag, CHARFORMAT2 structure minimally filled */
   memset(&cf2, 0, sizeof(cf2));
@@ -439,6 +643,9 @@
   rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
              (LPARAM) &cf2);
   ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
+  rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
+  todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
+  SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
   cf2.cbSize = sizeof(CHARFORMAT2);
   SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
@@ -492,6 +699,251 @@
   ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
   DestroyWindow(hwndRichEdit);
+
+  /* EM_GETCHARFORMAT tests */
+  for (i = 0; tested_effects[i]; i++)
+  {
+    hwndRichEdit = new_richedit(NULL);
+    SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
+
+    /* Need to set a TrueType font to get consistent CFM_BOLD results */
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    cf2.dwMask = CFM_FACE|CFM_WEIGHT;
+    cf2.dwEffects = 0;
+    strcpy(cf2.szFaceName, "Courier New");
+    cf2.wWeight = FW_DONTCARE;
+    SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
+    SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+    ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT)
&&
+          (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
+          ||
+          (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
+        "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
tested_effects[i]);
+    ok((cf2.dwEffects & tested_effects[i]) == 0,
+        "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i,
cf2.dwEffects, tested_effects[i]);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    cf2.dwMask = tested_effects[i];
+    if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
+      cf2.dwMask = CFM_SUPERSCRIPT;
+    cf2.dwEffects = tested_effects[i];
+    SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
+    SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
+    SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+    ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT)
&&
+          (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
+          ||
+          (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
+        "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
tested_effects[i]);
+    ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
+        "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i,
cf2.dwEffects, tested_effects[i]);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
+    SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+    ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT)
&&
+          (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
+          ||
+          (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
+        "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
tested_effects[i]);
+    ok((cf2.dwEffects & tested_effects[i]) == 0,
+        "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i,
cf2.dwEffects, tested_effects[i]);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
+    SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+    ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT)
&&
+          (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
+          ||
+          (cf2.dwMask & tested_effects[i]) == 0),
+        "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask,
tested_effects[i]);
+
+    DestroyWindow(hwndRichEdit);
+  }
+
+  for (i = 0; tested_effects[i]; i++)
+  {
+    hwndRichEdit = new_richedit(NULL);
+    SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
+
+    /* Need to set a TrueType font to get consistent CFM_BOLD results */
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    cf2.dwMask = CFM_FACE|CFM_WEIGHT;
+    cf2.dwEffects = 0;
+    strcpy(cf2.szFaceName, "Courier New");
+    cf2.wWeight = FW_DONTCARE;
+    SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    cf2.dwMask = tested_effects[i];
+    if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
+      cf2.dwMask = CFM_SUPERSCRIPT;
+    cf2.dwEffects = tested_effects[i];
+    SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
+    SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
+    SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+    ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT)
&&
+          (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
+          ||
+          (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
+        "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
tested_effects[i]);
+    ok((cf2.dwEffects & tested_effects[i]) == 0,
+        "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i,
cf2.dwEffects, tested_effects[i]);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
+    SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+    ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT)
&&
+          (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
+          ||
+          (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
+        "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
tested_effects[i]);
+    ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
+        "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i,
cf2.dwEffects, tested_effects[i]);
+
+    memset(&cf2, 0, sizeof(CHARFORMAT2));
+    cf2.cbSize = sizeof(CHARFORMAT2);
+    SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
+    SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+    ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT)
&&
+          (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
+          ||
+          (cf2.dwMask & tested_effects[i]) == 0),
+        "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask,
tested_effects[i]);
+    ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
+        "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i,
cf2.dwEffects, tested_effects[i]);
+
+    DestroyWindow(hwndRichEdit);
+  }
+
+  /* Effects applied on an empty selection should take effect when selection is
+     replaced with text */
+  hwndRichEdit = new_richedit(NULL);
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
+  SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  cf2.dwMask = CFM_BOLD;
+  cf2.dwEffects = CFE_BOLD;
+  SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  /* Selection is now nonempty */
+  SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
+  SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
+      "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
CFM_BOLD);
+  ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
+      "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects,
CFE_BOLD);
+
+
+  /* Set two effects on an empty selection */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
+  SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  cf2.dwMask = CFM_BOLD;
+  cf2.dwEffects = CFE_BOLD;
+  SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+  cf2.dwMask = CFM_ITALIC;
+  cf2.dwEffects = CFE_ITALIC;
+  SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  /* Selection is now nonempty */
+  SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
+  SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
+      "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
(CFM_BOLD|CFM_ITALIC));
+  ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
+      "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects,
(CFE_BOLD|CFE_ITALIC));
+
+  /* Setting the (empty) selection to exactly the same place as before should
+     NOT clear the insertion style! */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
+  SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  cf2.dwMask = CFM_BOLD;
+  cf2.dwEffects = CFE_BOLD;
+  SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  /* Empty selection in same place, insert style should NOT be forgotten here. */
+  SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
+
+  /* Selection is now nonempty */
+  SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
+  SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
+      "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
CFM_BOLD);
+  ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
+      "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects,
CFE_BOLD);
+
+  /* Ditto with EM_EXSETSEL */
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
+  cr.cpMin = 2; cr.cpMax = 2;
+  SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  cf2.dwMask = CFM_BOLD;
+  cf2.dwEffects = CFE_BOLD;
+  SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  /* Empty selection in same place, insert style should NOT be forgotten here. */
+  cr.cpMin = 2; cr.cpMax = 2;
+  SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
+
+  /* Selection is now nonempty */
+  SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
+
+  memset(&cf2, 0, sizeof(CHARFORMAT2));
+  cf2.cbSize = sizeof(CHARFORMAT2);
+  cr.cpMin = 2; cr.cpMax = 6;
+  SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
+  SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
+
+  ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
+      "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask,
CFM_BOLD);
+  ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
+      "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects,
CFE_BOLD);
+
+  DestroyWindow(hwndRichEdit);
 }
 static void test_EM_SETTEXTMODE(void)
@@ -901,14 +1353,20 @@
     DestroyWindow(hwndRichEdit);
 }
+static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
+{
+  CHARFORMAT2W text_format;
+  text_format.cbSize = sizeof(text_format);
+  SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
+  SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
+  return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
+}
+
 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
 {
-  CHARFORMAT2W text_format;
   int link_present = 0;
-  text_format.cbSize = sizeof(text_format);
-  SendMessage(hwnd, EM_SETSEL, 0, 1);
-  SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
-  link_present = text_format.dwEffects & CFE_LINK;
+
+  link_present = check_CFE_LINK_selection(hwnd, 0, 1);
   if (is_url)
   { /* control text is url; should get CFE_LINK */
        ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
@@ -943,9 +1401,69 @@
     {"wais:waisserver", 1}
   };
-  int i;
+  int i, j;
   int urlRet=-1;
   HWND hwndRichEdit, parent;
+
+  /* All of the following should cause the URL to be detected  */
+  const char * templates_delim[] = {
+    "This is some text with X on it",
+    "This is some text with (X) on it",
+    "This is some text with X\r on it",
+    "This is some text with ---X--- on it",
+    "This is some text with \"X\" on it",
+    "This is some text with 'X' on it",
+    "This is some text with 'X' on it",
+    "This is some text with :X: on it",
+
+    "This text ends with X",
+
+    "This is some text with X) on it",
+    "This is some text with X--- on it",
+    "This is some text with X\" on it",
+    "This is some text with X' on it",
+    "This is some text with X: on it",
+
+    "This is some text with (X on it",
+    "This is some text with \rX on it",
+    "This is some text with ---X on it",
+    "This is some text with \"X on it",
+    "This is some text with 'X on it",
+    "This is some text with :X on it",
+  };
+  /* None of these should cause the URL to be detected */
+  const char * templates_non_delim[] = {
+    "This is some text with |X| on it",
+    "This is some text with *X* on it",
+    "This is some text with /X/ on it",
+    "This is some text with +X+ on it",
+    "This is some text with %X% on it",
+    "This is some text with #X# on it",
+    "This is some text with @X@ on it",
+    "This is some text with \\X\\ on it",
+    "This is some text with |X on it",
+    "This is some text with *X on it",
+    "This is some text with /X on it",
+    "This is some text with +X on it",
+    "This is some text with %X on it",
+    "This is some text with #X on it",
+    "This is some text with @X on it",
+    "This is some text with \\X on it",
+  };
+  /* All of these cause the URL detection to be extended by one more byte,
+     thus demonstrating that the tested character is considered as part
+     of the URL. */
+  const char * templates_xten_delim[] = {
+    "This is some text with X| on it",
+    "This is some text with X* on it",
+    "This is some text with X/ on it",
+    "This is some text with X+ on it",
+    "This is some text with X% on it",
+    "This is some text with X# on it",
+    "This is some text with X@ on it",
+    "This is some text with X\\ on it",
+  };
+  char buffer[1024];
   parent = new_static_wnd(NULL);
   hwndRichEdit = new_richedit(parent);
@@ -961,16 +1479,612 @@
   ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
   /* for each url, check the text to see if CFE_LINK effect is present */
   for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
+
     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
-    SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
     check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
+
+    /* Link detection should happen immediately upon WM_SETTEXT */
     SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
-    SendMessage(hwndRichEdit, WM_CHAR, 0, 0);
     check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
   }
   DestroyWindow(hwndRichEdit);
+
+  /* Test detection of URLs within normal text - WM_SETTEXT case. */
+  for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
+    hwndRichEdit = new_richedit(parent);
+
+    for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      at_pos = strchr(templates_delim[j], 'X');
+      at_offset = at_pos - templates_delim[j];
+      strncpy(buffer, templates_delim[j], at_offset);
+      buffer[at_offset] = '\0';
+      strcat(buffer, urls[i].text);
+      strcat(buffer, templates_delim[j] + at_offset + 1);
+      end_offset = at_offset + strlen(urls[i].text);
+
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+
+    for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      at_pos = strchr(templates_non_delim[j], 'X');
+      at_offset = at_pos - templates_non_delim[j];
+      strncpy(buffer, templates_non_delim[j], at_offset);
+      buffer[at_offset] = '\0';
+      strcat(buffer, urls[i].text);
+      strcat(buffer, templates_non_delim[j] + at_offset + 1);
+      end_offset = at_offset + strlen(urls[i].text);
+
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset
+ 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+
+    for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      at_pos = strchr(templates_xten_delim[j], 'X');
+      at_offset = at_pos - templates_xten_delim[j];
+      strncpy(buffer, templates_xten_delim[j], at_offset);
+      buffer[at_offset] = '\0';
+      strcat(buffer, urls[i].text);
+      strcat(buffer, templates_xten_delim[j] + at_offset + 1);
+      end_offset = at_offset + strlen(urls[i].text);
+
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset +1, buffer);
+      }
+      if (buffer[end_offset +1] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset + 2, buffer);
+        if (buffer[end_offset +2] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2,
end_offset +3, buffer);
+        }
+      }
+    }
+
+    DestroyWindow(hwndRichEdit);
+    hwndRichEdit = NULL;
+  }
+
+  /* Test detection of URLs within normal text - WM_CHAR case. */
+  for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
+    hwndRichEdit = new_richedit(parent);
+
+    for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+      int u, v;
+
+      at_pos = strchr(templates_delim[j], 'X');
+      at_offset = at_pos - templates_delim[j];
+      end_offset = at_offset + strlen(urls[i].text);
+
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+      for (u = 0; templates_delim[j][u]; u++) {
+        if (templates_delim[j][u] == '\r') {
+          simulate_typing_characters(hwndRichEdit, "\r");
+        } else if (templates_delim[j][u] != 'X') {
+          SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
+        } else {
+          for (v = 0; urls[i].text[v]; v++) {
+            SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
+          }
+        }
+      }
+      SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+      trace("Using template: %s\n", templates_delim[j]);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+
+      /* The following will insert a paragraph break after the first character
+         of the URL candidate, thus breaking the URL. It is expected that the
+         CFE_LINK attribute should break across both pieces of the URL */
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
+      simulate_typing_characters(hwndRichEdit, "\r");
+      SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset
+ 1, buffer);
+      /* end_offset moved because of paragraph break */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset+1, buffer);
+      ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it
missing a newline character?\n", buffer);
+      if (buffer[end_offset] != 0  && buffer[end_offset+1] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1,
end_offset +2, buffer);
+        if (buffer[end_offset +2] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2,
end_offset +3, buffer);
+        }
+      }
+
+      /* The following will remove the just-inserted paragraph break, thus
+         restoring the URL */
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
+      simulate_typing_characters(hwndRichEdit, "\b");
+      SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+    DestroyWindow(hwndRichEdit);
+    hwndRichEdit = NULL;
+  }
+
+  /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
+  for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
+    SETTEXTEX st;
+
+    hwndRichEdit = new_richedit(parent);
+
+    /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
+       be detected:
+       1) Set entire text, a la WM_SETTEXT
+       2) Set a selection of the text to the URL
+       3) Set a portion of the text at a time, which eventually results in
+          an URL
+       All of them should give equivalent results
+     */
+
+    /* Set entire text in one go, like WM_SETTEXT */
+    for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      st.codepage = CP_ACP;
+      st.flags = ST_DEFAULT;
+
+      at_pos = strchr(templates_delim[j], 'X');
+      at_offset = at_pos - templates_delim[j];
+      strncpy(buffer, templates_delim[j], at_offset);
+      buffer[at_offset] = '\0';
+      strcat(buffer, urls[i].text);
+      strcat(buffer, templates_delim[j] + at_offset + 1);
+      end_offset = at_offset + strlen(urls[i].text);
+
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+
+    /* Set selection with X to the URL */
+    for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      at_pos = strchr(templates_delim[j], 'X');
+      at_offset = at_pos - templates_delim[j];
+      end_offset = at_offset + strlen(urls[i].text);
+
+      st.codepage = CP_ACP;
+      st.flags = ST_DEFAULT;
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)
templates_delim[j]);
+      st.flags = ST_SELECTION;
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
+      SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
+      SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+
+    /* Set selection with X to the first character of the URL, then the rest */
+    for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      at_pos = strchr(templates_delim[j], 'X');
+      at_offset = at_pos - templates_delim[j];
+      end_offset = at_offset + strlen(urls[i].text);
+
+      strcpy(buffer, "YY");
+      buffer[0] = urls[i].text[0];
+
+      st.codepage = CP_ACP;
+      st.flags = ST_DEFAULT;
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)
templates_delim[j]);
+      st.flags = ST_SELECTION;
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
+      SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
+      SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text +
1));
+      SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+
+    DestroyWindow(hwndRichEdit);
+    hwndRichEdit = NULL;
+  }
+
+  /* Test detection of URLs within normal text - EM_REPLACESEL case. */
+  for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
+    hwndRichEdit = new_richedit(parent);
+
+    /* Set selection with X to the URL */
+    for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      at_pos = strchr(templates_delim[j], 'X');
+      at_offset = at_pos - templates_delim[j];
+      end_offset = at_offset + strlen(urls[i].text);
+
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
+      SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
+      SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+
+    /* Set selection with X to the first character of the URL, then the rest */
+    for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
+      char * at_pos;
+      int at_offset;
+      int end_offset;
+
+      at_pos = strchr(templates_delim[j], 'X');
+      at_offset = at_pos - templates_delim[j];
+      end_offset = at_offset + strlen(urls[i].text);
+
+      strcpy(buffer, "YY");
+      buffer[0] = urls[i].text[0];
+
+      SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
+      SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
+      SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
+      SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
+      SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+
+      /* This assumes no templates start with the URL itself, and that they
+         have at least two characters before the URL text */
+      ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2,
at_offset -1, buffer);
+      ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
+        "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1,
at_offset, buffer);
+
+      if (urls[i].is_url)
+      {
+        ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1,
buffer);
+        ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset,
buffer);
+      }
+      else
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset,
at_offset + 1, buffer);
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1,
end_offset, buffer);
+      }
+      if (buffer[end_offset] != '\0')
+      {
+        ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
+          "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset,
end_offset + 1, buffer);
+        if (buffer[end_offset +1] != '\0')
+        {
+          ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
+            "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1,
end_offset +2, buffer);
+        }
+      }
+    }
+
+    DestroyWindow(hwndRichEdit);
+    hwndRichEdit = NULL;
+  }
+
   DestroyWindow(parent);
 }
@@ -2353,23 +3467,18 @@
     r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
     ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
+    if (!redraw)
+        /* This is needed to avoid interferring with keybd_event calls
+         * on other tests that simulate keyboard events. */
+        SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
+
     DestroyWindow(hwndRichEdit);
 }
 static void test_WM_PASTE(void)
 {
-    MSG msg;
     int result;
     char buffer[1024] = {0};
-    char key_info[][3] =
-    {
-        /* VirtualKey, ScanCode, WM_CHAR code */
-        {'C', 0x2e,  3},       /* Ctrl-C */
-        {'X', 0x2d, 24},       /* Ctrl-X */
-        {'V', 0x2f, 22},       /* Ctrl-V */
-        {'Z', 0x2c, 26},       /* Ctrl-Z */
-        {'Y', 0x15, 25},       /* Ctrl-Y */
-    };
     const char* text1 = "testing paste\r";
     const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
     const char* text1_after = "testing paste\r\n";
@@ -2382,34 +3491,25 @@
        messages, probably because it inspects the keyboard state itself.
        Therefore, native requires this in order to obey Ctrl-<key> keystrokes.
      */
-#define SEND_CTRL_KEY(hwnd, k) \
-    keybd_event(VK_CONTROL, 0x1d, 0, 0);\
-    keybd_event(k[0], k[1], 0, 0);\
-    keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\
-    keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \
-    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
-        TranslateMessage(&msg); \
-        DispatchMessage(&msg); \
-    }
-
-#define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0])
-#define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1])
-#define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2])
-#define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3])
-#define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4])
+
+#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')
+#define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
+#define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
     SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
-    SEND_CTRL_C(hwndRichEdit)   /* Copy */
+    SEND_CTRL_C(hwndRichEdit);   /* Copy */
     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
-    SEND_CTRL_V(hwndRichEdit)   /* Paste */
+    SEND_CTRL_V(hwndRichEdit);   /* Paste */
     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
     /* Pasted text should be visible at this step */
     result = strcmp(text1_step1, buffer);
     ok(result == 0,
         "test paste: strcmp = %i\n", result);
-    SEND_CTRL_Z(hwndRichEdit)   /* Undo */
+    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);
@@ -2418,21 +3518,21 @@
     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
     SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
-    SEND_CTRL_C(hwndRichEdit)   /* Copy */
+    SEND_CTRL_C(hwndRichEdit);   /* Copy */
     SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
-    SEND_CTRL_V(hwndRichEdit)   /* Paste */
+    SEND_CTRL_V(hwndRichEdit);   /* Paste */
     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
     /* Pasted text should be visible at this step */
     result = strcmp(text3, buffer);
     ok(result == 0,
         "test paste: strcmp = %i\n", result);
-    SEND_CTRL_Z(hwndRichEdit)   /* Undo */
+    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(text2_after, buffer);
     ok(result == 0,
         "test paste: strcmp = %i\n", result);
-    SEND_CTRL_Y(hwndRichEdit)   /* Redo */
+    SEND_CTRL_Y(hwndRichEdit);   /* Redo */
     SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
     /* Text should revert to post-paste state */
     result = strcmp(buffer,text3);
@@ -2531,6 +3631,30 @@
   return 0;
 }
+struct StringWithLength {
+    int length;
+    char *buffer;
+};
+
+/* This callback is used to handled the null characters in a string. */
+static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
+                                                   LPBYTE pbBuff,
+                                                   LONG cb,
+                                                   LONG *pcb)
+{
+    struct StringWithLength* str = (struct StringWithLength*)dwCookie;
+    int size = str->length;
+    *pcb = cb;
+    if (*pcb > size) {
+      *pcb = size;
+    }
+    if (*pcb > 0) {
+      memcpy(pbBuff, str->buffer, *pcb);
+      str->buffer += *pcb;
+      str->length -= *pcb;
+    }
+    return 0;
+}
 static void test_EM_STREAMIN(void)
 {
@@ -2560,6 +3684,15 @@
   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 seperate lines and make sure the null terminating character is "\
+      "handled properly.\0";
+  int length4 = strlen(streamText4) + 1;
+  cookieForStream4.buffer = (char *)streamText4;
+  cookieForStream4.length = length4;
+
   /* Minimal test without \par at the end */
   es.dwCookie = (DWORD_PTR)&streamText0;
   es.dwError = 0;
@@ -2642,6 +3775,17 @@
   ok (strlen(buffer)  == 0,
       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
   ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n",
es.dwError, -16);
+
+  es.dwCookie = (DWORD_PTR)&cookieForStream4;
+  es.dwError = 0;
+  es.pfnCallback = test_EM_STREAMIN_esCallback2;
+  SendMessage(hwndRichEdit, EM_STREAMIN,
+              (WPARAM)(SF_TEXT), (LPARAM)&es);
+
+  result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
+  ok (result  == length4,
+      "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
+  ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n",
es.dwError, 0);
   DestroyWindow(hwndRichEdit);
 }
@@ -2718,6 +3862,7 @@
       "EM_STREAMIN with SFF_SELECTION and selection set "
       "should create an undo\n");
+  DestroyWindow(hwndRichEdit);
 }
 static BOOL is_em_settextex_supported(HWND hwnd)
@@ -3208,6 +4353,203 @@
     DestroyWindow(parent);
 }
+static void test_undo_coalescing(void)
+{
+    HWND hwnd;
+    int result;
+    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);
+    ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
+
+    result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
+    ok (result == FALSE, "Can undo after window creation.\n");
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
+    result = SendMessage(hwnd, EM_CANREDO, 0, 0);
+    ok (result == FALSE, "Can redo after window creation.\n");
+    result = SendMessage(hwnd, EM_REDO, 0, 0);
+    ok (result == FALSE, "Redo operation successful with nothing undone.\n");
+
+    /* Test the effect of arrows keys during typing on undo transactions*/
+    simulate_typing_characters(hwnd, "one two three");
+    SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
+    SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
+    simulate_typing_characters(hwnd, " four five six");
+
+    result = SendMessage(hwnd, EM_CANREDO, 0, 0);
+    ok (result == FALSE, "Can redo before anything is undone.\n");
+    result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
+    ok (result == TRUE, "Cannot undo typed characters.\n");
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
+    result = SendMessage(hwnd, EM_CANREDO, 0, 0);
+    ok (result == TRUE, "Cannot redo after undo.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "one two three");
+    ok (result == 0, "expected '%s' but got '%s'\n", "one
two three", buffer);
+
+    result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
+    ok (result == TRUE, "Cannot undo typed characters.\n");
+    result = SendMessage(hwnd, WM_UNDO, 0, 0);
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "");
+    ok (result == 0, "expected '%s' but got '%s'\n",
"", buffer);
+
+    /* Test the effect of focus changes during typing on undo transactions*/
+    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);
+    simulate_typing_characters(hwnd, " four five six");
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "one two three");
+    ok (result == 0, "expected '%s' but got '%s'\n", "one
two three", buffer);
+
+    /* Test the effect of the back key during typing on undo transactions */
+    SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
+    result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
+    ok (result == TRUE, "Failed to clear the text.\n");
+    simulate_typing_characters(hwnd, "one two threa");
+    result = SendMessage(hwnd, EM_CANREDO, 0, 0);
+    ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
+    SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
+    SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
+    simulate_typing_characters(hwnd, "e four five six");
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "");
+    ok (result == 0, "expected '%s' but got '%s'\n",
"", buffer);
+
+    /* Test the effect of the delete key during typing on undo transactions */
+    SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
+    result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
+    ok(result == TRUE, "Failed to set the text.\n");
+    SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
+    SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
+    SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
+    SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
+    SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "acd");
+    ok (result == 0, "expected '%s' but got '%s'\n",
"acd", buffer);
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "abcd");
+    ok (result == 0, "expected '%s' but got '%s'\n",
"abcd", buffer);
+
+    /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
+    SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
+    result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
+    ok (result == TRUE, "Failed to clear the text.\n");
+    simulate_typing_characters(hwnd, "one two three");
+    result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
+    ok (result == 0, "expected %d but got %d\n", 0, result);
+    simulate_typing_characters(hwnd, " four five six");
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "one two three");
+    ok (result == 0, "expected '%s' but got '%s'\n", "one
two three", buffer);
+    result = SendMessage(hwnd, EM_UNDO, 0, 0);
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    ok (result == TRUE, "Failed to undo typed characters.\n");
+    SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    result = strcmp(buffer, "");
+    ok (result == 0, "expected '%s' but got '%s'\n",
"", buffer);
+
+    DestroyWindow(hwnd);
+}
+
+#define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
+#define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
+
+static void test_word_movement(void)
+{
+    HWND hwnd;
+    int result;
+    int sel_start, sel_end;
+
+    /* multi-line control inserts CR normally */
+    hwnd = new_richedit(NULL);
+
+    result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two  three");
+    ok (result == TRUE, "Failed to clear the text.\n");
+    SendMessage(hwnd, EM_SETSEL, 0, 0);
+    /* |one two three */
+
+    SEND_CTRL_RIGHT(hwnd);
+    /* one |two  three */
+    SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
+    ok(sel_start == sel_end, "Selection should be empty\n");
+    ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
+
+    SEND_CTRL_RIGHT(hwnd);
+    /* one two  |three */
+    SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
+    ok(sel_start == sel_end, "Selection should be empty\n");
+    ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
+
+    SEND_CTRL_LEFT(hwnd);
+    /* one |two  three */
+    SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
+    ok(sel_start == sel_end, "Selection should be empty\n");
+    ok(sel_start == 4, "Cursur is at %d instead of %d\n", sel_start, 4);
+
+    SEND_CTRL_LEFT(hwnd);
+    /* |one two  three */
+    SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
+    ok(sel_start == sel_end, "Selection should be empty\n");
+    ok(sel_start == 0, "Cursur is at %d instead of %d\n", sel_start, 0);
+
+    SendMessage(hwnd, EM_SETSEL, 8, 8);
+    /* one two | three */
+    SEND_CTRL_RIGHT(hwnd);
+    /* one two  |three */
+    SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
+    ok(sel_start == sel_end, "Selection should be empty\n");
+    ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
+
+    SendMessage(hwnd, EM_SETSEL, 11, 11);
+    /* one two  th|ree */
+    SEND_CTRL_LEFT(hwnd);
+    /* one two  |three */
+    SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
+    ok(sel_start == sel_end, "Selection should be empty\n");
+    ok(sel_start == 9, "Cursur is at %d instead of %d\n", sel_start, 9);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_EM_CHARFROMPOS(void)
+{
+    HWND hwnd;
+    int result;
+    POINTL point;
+    point.x = 0;
+    point.y = 50;
+
+    /* multi-line control inserts CR normally */
+    hwnd = new_richedit(NULL);
+    result = SendMessageA(hwnd, WM_SETTEXT, 0,
+                          (LPARAM)"one two three four five six seven");
+
+    result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
+    ok(result == 0, "expected character index of 0 but got %d\n", result);
+
+    DestroyWindow(hwnd);
+}
+
 START_TEST( editor )
 {
   MSG msg;
@@ -3220,6 +4562,7 @@
   test_WM_CHAR();
   test_EM_FINDTEXT();
   test_EM_GETLINE();
+  test_EM_POSFROMCHAR();
   test_EM_SCROLLCARET();
   test_EM_SCROLL();
   test_WM_SETTEXT();
@@ -3231,7 +4574,6 @@
   test_WM_GETTEXT();
   test_EM_GETTEXTRANGE();
   test_EM_GETSELTEXT();
-  test_EM_AUTOURLDETECT();
   test_EM_SETUNDOLIMIT();
   test_ES_PASSWORD();
   test_EM_SETTEXTEX();
@@ -3251,7 +4593,11 @@
   test_EM_REPLACESEL(1);
   test_EM_REPLACESEL(0);
   test_WM_NOTIFY();
+  test_EM_AUTOURLDETECT();
   test_eventMask();
+  test_undo_coalescing();
+  test_word_movement();
+  test_EM_CHARFROMPOS();
   /* Set the environment variable WINETEST_RICHED20 to keep windows
    * responsive and open for 30 seconds. This is useful for debugging.
Modified: trunk/rostests/winetests/riched20/riched20.rbuild
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/riched20/riched…
==============================================================================
--- trunk/rostests/winetests/riched20/riched20.rbuild [iso-8859-1] (original)
+++ trunk/rostests/winetests/riched20/riched20.rbuild [iso-8859-1] Tue Jul  8 05:09:42
2008
@@ -6,8 +6,10 @@
        <define name="WINVER">0x600</define>
        <define name="_WIN32_WINNT">0x600</define>
        <file>editor.c</file>
+       <file>richole.c</file>
        <file>testlist.c</file>
        <library>wine</library>
+       <library>uuid</library>
        <library>ole32</library>
        <library>user32</library>
        <library>gdi32</library>
Added: trunk/rostests/winetests/riched20/richole.c
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/riched20/richol…
==============================================================================
--- trunk/rostests/winetests/riched20/richole.c (added)
+++ trunk/rostests/winetests/riched20/richole.c [iso-8859-1] Tue Jul  8 05:09:42 2008
@@ -1,0 +1,120 @@
+/*
+ * Tests for IRichEditOle and friends.
+ *
+ * Copyright 2008 Google (Dan Hipschman)
+ *
+ * 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 <stdarg.h>
+#include <assert.h>
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <ole2.h>
+#include <richedit.h>
+#include <richole.h>
+#include <tom.h>
+#include <wine/test.h>
+
+#include <initguid.h>
+DEFINE_GUID(IID_ITextDocument, 0x8cc497c0, 0xa1df, 0x11ce, 0x80, 0x98, 0x00, 0xaa, 0x00,
0x47, 0xbe, 0x5d);
+DEFINE_GUID(IID_ITextRange, 0x8cc497c2, 0xa1df, 0x11ce, 0x80, 0x98, 0x00, 0xaa, 0x00,
0x47, 0xbe, 0x5d);
+DEFINE_GUID(IID_ITextSelection, 0x8cc497c1, 0xa1df, 0x11ce, 0x80, 0x98, 0x00, 0xaa, 0x00,
0x47, 0xbe, 0x5d);
+
+static HMODULE hmoduleRichEdit;
+
+static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent)
+{
+  HWND hwnd
+    = CreateWindow(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());
+  return hwnd;
+}
+
+static HWND new_richedit(HWND parent)
+{
+  return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
+}
+
+
+START_TEST(richole)
+{
+  IRichEditOle *reOle = NULL;
+  ITextDocument *txtDoc = NULL;
+  ITextSelection *txtSel = NULL;
+  IUnknown *punk;
+  HRESULT hres;
+  LRESULT res;
+  HWND w;
+
+  /* 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());
+
+  w = new_richedit(NULL);
+  if (!w) {
+    skip("Couldn't create window\n");
+    return;
+  }
+
+  res = SendMessage(w, EM_GETOLEINTERFACE, 0, (LPARAM) &reOle);
+  ok(res, "SendMessage\n");
+  ok(reOle != NULL, "EM_GETOLEINTERFACE\n");
+
+  hres = IUnknown_QueryInterface(reOle, &IID_ITextDocument,
+                                 (void **) &txtDoc);
+  ok(hres == S_OK, "IRichEditOle_QueryInterface\n");
+  ok(txtDoc != NULL, "IRichEditOle_QueryInterface\n");
+
+  hres = ITextDocument_GetSelection(txtDoc, &txtSel);
+  ok(hres == S_OK, "ITextDocument_GetSelection\n");
+  ok(txtSel != NULL, "ITextDocument_GetSelection\n");
+
+  punk = NULL;
+  hres = ITextSelection_QueryInterface(txtSel, &IID_ITextSelection, (void **)
&punk);
+  ok(hres == S_OK, "ITextSelection_QueryInterface\n");
+  ok(punk != NULL, "ITextSelection_QueryInterface\n");
+  IUnknown_Release(punk);
+
+  punk = NULL;
+  hres = ITextSelection_QueryInterface(txtSel, &IID_ITextRange, (void **) &punk);
+  ok(hres == S_OK, "ITextSelection_QueryInterface\n");
+  ok(punk != NULL, "ITextSelection_QueryInterface\n");
+  IUnknown_Release(punk);
+
+  punk = NULL;
+  hres = ITextSelection_QueryInterface(txtSel, &IID_IDispatch, (void **) &punk);
+  ok(hres == S_OK, "ITextSelection_QueryInterface\n");
+  ok(punk != NULL, "ITextSelection_QueryInterface\n");
+  IUnknown_Release(punk);
+
+  ITextDocument_Release(txtDoc);
+  IUnknown_Release(reOle);
+  DestroyWindow(w);
+
+  /* Methods should return CO_E_RELEASED if the backing document has
+     been released.  One test should suffice.  */
+  hres = ITextSelection_CanEdit(txtSel, NULL);
+  ok(hres == CO_E_RELEASED, "ITextSelection after ITextDocument destroyed\n");
+
+  ITextSelection_Release(txtSel);
+}
Propchange: trunk/rostests/winetests/riched20/richole.c
------------------------------------------------------------------------------
    svn:eol-style = native
Modified: trunk/rostests/winetests/riched20/testlist.c
URL:
http://svn.reactos.org/svn/reactos/trunk/rostests/winetests/riched20/testli…
==============================================================================
--- trunk/rostests/winetests/riched20/testlist.c [iso-8859-1] (original)
+++ trunk/rostests/winetests/riched20/testlist.c [iso-8859-1] Tue Jul  8 05:09:42 2008
@@ -7,9 +7,11 @@
 #include "wine/test.h"
 extern void func_editor(void);
+extern void func_richole(void);
 const struct test winetest_testlist[] =
 {
     { "editor", func_editor },
+    { "richole", func_richole },
     { 0, 0 }
 };