[PATCH] richedit: WM_SETTEXT must immediately autodetect URLs, without waiting for a WM_CHAR

Alex Villacís Lasso alex at karlalex.palosanto.com
Fri May 2 00:06:10 CDT 2008


Tests show that native richedit does not need to wait for a WM_CHAR to be sent before autodetecting URLs. In addition, EM_AUTOURLDETECT tests were made more exhaustive in order to find out which characters can start and end an URL. Fixes URL not being highlighted in Perfect! TextEdit, and possibly other applications.
---
 dlls/riched20/editor.c       |  182 +++++++++++++++++++++++++++++++-
 dlls/riched20/editor.h       |    3 +
 dlls/riched20/tests/editor.c |  241 ++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 416 insertions(+), 10 deletions(-)

diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 7d57eca..4c41e69 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -2454,6 +2454,22 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
     }
     else
       TRACE("WM_SETTEXT - NULL\n");
+    if (editor->AutoURLDetect_bEnable)
+    {
+      int cMin = 0, cMax = -1;
+      while (ME_FindNextURLCandidate(editor, cMin, cMax, &cMin, &cMax))
+      {
+        if (ME_IsCandidateAnURL(editor, cMin, cMax)) {
+          CHARFORMAT2W link;
+          link.cbSize = sizeof(link);
+          link.dwMask = CFM_LINK;
+          link.dwEffects = CFE_LINK;
+          ME_SetCharFormat(editor, cMin, cMax - cMin, &link);
+        }
+        cMin = cMax;
+        cMax = -1;
+      }
+    }
     ME_SetSelection(editor, 0, 0);
     editor->nModifyStep = 0;
     ME_CommitUndo(editor);
@@ -3449,9 +3465,11 @@ int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, in
     CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
     nChars -= nLen;
     nWritten += nLen;
-    if (!nChars)
-      return nWritten;
     buffer += nLen;
+    if (!nChars) {
+      *buffer = 0;
+      return nWritten;
+    }
     nStart = 0;
     item = ME_FindItemFwd(item, diRun);
   }
@@ -3713,3 +3731,163 @@ int ME_AutoURLDetect(ME_TextEditor *editor, WCHAR curChar)
   }
   return 0;
 }
+
+
+static BOOL isurlspecial(WCHAR c)
+{
+  static const char * special_chars = "./%@*|\\+#";
+  const char * p = special_chars;
+  
+  while (*p != '\0') if (*p++ == c) return TRUE;  
+  return FALSE;
+}
+
+/**
+ * This proc takes a selection, and scans it forward in order to select the span
+ * of a possible URL candidate. A possible URL candidate must start with isalnum
+ * or one of the following special characters: *|/\+%#@ and must consist entirely
+ * of the characters allowed to start the URL, plus : (colon) which may occur
+ * at most once, and not at either end.
+ *
+ * sel_max == -1 indicates scan to end of text.
+ */
+BOOL ME_FindNextURLCandidate(ME_TextEditor *editor, int sel_min, int sel_max, 
+        int * candidate_min, int * candidate_max)
+{
+  ME_DisplayItem * item;
+  ME_DisplayItem * para;
+  int nStart;
+  BOOL foundColon = FALSE;
+  WCHAR lastAcceptedChar = '\0';
+
+  TRACE("sel_min = %d sel_max = %d\n", sel_min, sel_max);
+
+  *candidate_min = *candidate_max = -1;
+  item = ME_FindItemAtOffset(editor, diRun, sel_min, &nStart);
+  if (!item) return FALSE;
+  TRACE("nStart = %d\n", nStart);
+  para = ME_GetParagraph(item);
+  if (sel_max == -1) sel_max = ME_GetTextLength(editor);
+  while (item && para->member.para.nCharOfs + item->member.run.nCharOfs + nStart < sel_max)
+  {
+    ME_DisplayItem * next_item;
+    
+    if (!(item->member.run.nFlags & MERF_ENDPARA)) {
+      /* Find start of candidate */
+      if (*candidate_min == -1) {
+        while (nStart < ME_StrLen(item->member.run.strText) &&
+                !(isalnumW(item->member.run.strText->szData[nStart]) || 
+                  isurlspecial(item->member.run.strText->szData[nStart]))) {
+          nStart++;
+        }
+        if (nStart < ME_StrLen(item->member.run.strText) &&
+                (isalnumW(item->member.run.strText->szData[nStart]) || 
+                 isurlspecial(item->member.run.strText->szData[nStart]))) {
+          *candidate_min = para->member.para.nCharOfs + item->member.run.nCharOfs + nStart;
+          lastAcceptedChar = item->member.run.strText->szData[nStart];
+          nStart++;
+        }
+      }
+      
+      /* Find end of candidate */
+      if (*candidate_min >= 0) {
+        while (nStart < ME_StrLen(item->member.run.strText) &&
+                (isalnumW(item->member.run.strText->szData[nStart]) || 
+                 isurlspecial(item->member.run.strText->szData[nStart]) ||
+                 (!foundColon && item->member.run.strText->szData[nStart] == ':') )) {
+          if (item->member.run.strText->szData[nStart] == ':') foundColon = TRUE;
+          lastAcceptedChar = item->member.run.strText->szData[nStart];
+          nStart++;
+        }
+        if (nStart < ME_StrLen(item->member.run.strText) &&
+                !(isalnumW(item->member.run.strText->szData[nStart]) || 
+                 isurlspecial(item->member.run.strText->szData[nStart]) )) {
+          *candidate_max = para->member.para.nCharOfs + item->member.run.nCharOfs + nStart;
+          nStart++;
+          if (lastAcceptedChar == ':') (*candidate_max)--;
+          return TRUE;
+        }        
+      }
+    } else {
+      /* End of paragraph: skip it if before candidate span, or terminates
+         current active span */
+      if (*candidate_min >= 0) {
+        *candidate_max = para->member.para.nCharOfs + item->member.run.nCharOfs;
+        if (lastAcceptedChar == ':') (*candidate_max)--;
+        return TRUE;
+      }
+    }
+    
+    /* Reaching this point means no span was found, so get next span */
+    next_item = ME_FindItemFwd(item, diRun);
+    if (!next_item) {
+      if (*candidate_min >= 0) {
+        /* There are no further runs, so take end of text as end of candidate */
+        *candidate_max = para->member.para.nCharOfs + item->member.run.nCharOfs + nStart;
+        if (lastAcceptedChar == ':') (*candidate_max)--;
+        return TRUE;
+      }
+    }
+    item = next_item;
+    para = ME_GetParagraph(item);
+    nStart = 0;
+  }
+
+  if (item) {
+    if (*candidate_min >= 0) {
+      /* There are no further runs, so take end of text as end of candidate */
+      *candidate_max = para->member.para.nCharOfs + item->member.run.nCharOfs + nStart;
+      if (lastAcceptedChar == ':') (*candidate_max)--;
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * This proc evaluates the selection and returns TRUE if it can be considered an URL
+ */
+BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, int sel_min, int sel_max)
+{
+  struct prefix_s {
+    const char *text;
+    int length;
+  } prefixes[12] = {
+    /* Code below depends on these being in decreasing length order! */
+    {"prospero:", 10},
+    {"telnet:", 8},
+    {"gopher:", 8},
+    {"mailto:", 8},
+    {"https:", 7},
+    {"file:", 6},
+    {"news:", 6},
+    {"wais:", 6},
+    {"nntp:", 6},
+    {"http:", 5},
+    {"www.", 5},
+    {"ftp:", 5},
+  };
+  LPWSTR bufferW = NULL;
+  WCHAR bufW[32];
+  int i;
+  
+  if (sel_max == -1) sel_max = ME_GetTextLength(editor);
+  assert(sel_min <= sel_max);  
+  for (i = 0; i < sizeof(prefixes) / sizeof(struct prefix_s); i++)
+  {
+    if (sel_max - sel_min < prefixes[i].length) continue;
+    if (bufferW == NULL) {
+      bufferW = (LPWSTR)heap_alloc((sel_max - sel_min + 1) * sizeof(WCHAR));
+    }
+    ME_GetTextW(editor, bufferW, sel_min, min(sel_max - sel_min, strlen(prefixes[i].text)), 0);
+    MultiByteToWideChar(CP_ACP, 0, prefixes[i].text, -1, bufW, 32);
+    if (!lstrcmpW(bufW, bufferW))
+    {
+      heap_free(bufferW);
+      return TRUE;
+    }
+  }
+  if (bufferW != NULL) heap_free(bufferW);
+  return FALSE;
+}
+
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
index 992ce7f..e274a95 100644
--- a/dlls/riched20/editor.h
+++ b/dlls/riched20/editor.h
@@ -283,6 +283,9 @@ void ME_StreamInFill(ME_InStream *stream);
 int ME_AutoURLDetect(ME_TextEditor *editor, WCHAR curChar);
 extern int me_debug;
 extern void DoWrap(ME_TextEditor *editor);
+extern BOOL ME_FindNextURLCandidate(ME_TextEditor *editor, int sel_min, int sel_max, 
+        int * candidate_min, int * candidate_max);
+extern BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, int sel_min, int sel_max);
 
 /* writer.c */
 LRESULT ME_StreamOutRange(ME_TextEditor *editor, DWORD dwFormat, int nStart, int nTo, EDITSTREAM *stream);
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 075ac2f..ec5a9e1 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -933,14 +933,20 @@ static void test_EM_SETOPTIONS(void)
     DestroyWindow(hwndRichEdit);
 }
 
-static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
+static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
 {
   CHARFORMAT2W text_format;
-  int link_present = 0;
   text_format.cbSize = sizeof(text_format);
-  SendMessage(hwnd, EM_SETSEL, 0, 1);
+  SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
   SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
-  link_present = text_format.dwEffects & CFE_LINK;
+  return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
+}
+
+static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
+{
+  int link_present = 0;
+
+  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);
@@ -975,10 +981,70 @@ static void test_EM_AUTOURLDETECT(void)
     {"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);
   /* Try and pass EM_AUTOURLDETECT some test wParam values */
@@ -993,16 +1059,175 @@ static void test_EM_AUTOURLDETECT(void)
   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;
+  }
+
+
+//  DestroyWindow(hwndRichEdit);
   DestroyWindow(parent);
 }
 
@@ -3263,7 +3488,6 @@ START_TEST( editor )
   test_WM_GETTEXT();
   test_EM_GETTEXTRANGE();
   test_EM_GETSELTEXT();
-  test_EM_AUTOURLDETECT();
   test_EM_SETUNDOLIMIT();
   test_ES_PASSWORD();
   test_EM_SETTEXTEX();
@@ -3274,6 +3498,7 @@ START_TEST( editor )
   test_EM_GETMODIFY();
   test_EM_EXSETSEL();
   test_WM_PASTE();
+  test_EM_AUTOURLDETECT();
   test_EM_STREAMIN();
   test_EM_STREAMOUT();
   test_EM_StreamIn_Undo();
-- 
1.5.4.1

------=_20080502130806_13844--





More information about the wine-patches mailing list