richedit: Add more tests for URL autodetection on WM_CHAR, make them pass under Wine.
authorAlex Villacís Lasso <a_villacis@palosanto.com>
Wed, 18 Jun 2008 16:05:52 +0000 (11:05 -0500)
committerAlexandre Julliard <julliard@winehq.org>
Thu, 19 Jun 2008 10:07:36 +0000 (12:07 +0200)
dlls/riched20/editor.c
dlls/riched20/editor.h
dlls/riched20/tests/editor.c

index edd6be6da8873cf95bb0e82fca15c38b482f2622..fc6e4eac579f5caa1de16a196aa4ad341f6ce243 100644 (file)
@@ -1558,6 +1558,7 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey)
       else
         return TRUE;
       ME_CommitUndo(editor);
+      ME_UpdateSelectionLinkAttribute(editor);
       ME_UpdateRepaint(editor);
       ME_SendRequestResize(editor, FALSE);
       return TRUE;
@@ -2456,19 +2457,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
       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_UpdateLinkAttribute(editor, 0, -1);
     }
     ME_SetSelection(editor, 0, 0);
     editor->nModifyStep = 0;
@@ -3052,9 +3041,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
         CHAR charA = wParam;
         MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1);
     }
-    if (editor->AutoURLDetect_bEnable)
-      ME_AutoURLDetect(editor, wstr);
-        
+
     switch (wstr)
     {
     case 1: /* Ctrl-A */
@@ -3103,6 +3090,9 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
         ME_ReleaseStyle(style);
         ME_CommitUndo(editor);
       }
+
+      if (editor->AutoURLDetect_bEnable) ME_UpdateSelectionLinkAttribute(editor);
+
       ME_UpdateRepaint(editor);
     }
     return 0;
@@ -3887,3 +3877,128 @@ BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, int sel_min, int sel_max)
   if (bufferW != NULL) heap_free(bufferW);
   return FALSE;
 }
+
+/**
+ * This proc walks through the indicated selection and evaluates whether each
+ * section identified by ME_FindNextURLCandidate and in-between sections have
+ * their proper CFE_LINK attributes set or unset. If the CFE_LINK attribute is
+ * not what it is supposed to be, this proc sets or unsets it as appropriate.
+ *
+ * Returns TRUE if at least one section was modified.
+ */
+BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, int sel_min, int sel_max)
+{
+  BOOL modified = FALSE;
+  int cMin, cMax;
+
+  if (sel_max == -1) sel_max = ME_GetTextLength(editor);
+  do
+  {
+    int beforeURL[2];
+    int inURL[2];
+    CHARFORMAT2W link;
+
+    if (ME_FindNextURLCandidate(editor, sel_min, sel_max, &cMin, &cMax))
+    {
+      /* Section before candidate is not an URL */
+      beforeURL[0] = sel_min;
+      beforeURL[1] = cMin;
+
+      if (ME_IsCandidateAnURL(editor, cMin, cMax))
+      {
+        inURL[0] = cMin; inURL[1] = cMax;
+      }
+      else
+      {
+        beforeURL[1] = cMax;
+        inURL[0] = inURL[1] = -1;
+      }
+      sel_min = cMax;
+    }
+    else
+    {
+      /* No more candidates until end of selection */
+      beforeURL[0] = sel_min;
+      beforeURL[1] = sel_max;
+      inURL[0] = inURL[1] = -1;
+      sel_min = sel_max;
+    }
+
+    if (beforeURL[0] < beforeURL[1])
+    {
+      /* CFE_LINK effect should be consistently unset */
+      link.cbSize = sizeof(link);
+      ME_GetCharFormat(editor, beforeURL[0], beforeURL[1], &link);
+      /* FIXME: Workaround for what looks like a bug - ME_GetCharFormat does not
+         clear the CFM_LINK flag when selection spans text without CFE_LINK,
+         followed by CFE_LINK set. This needs a test for EM_GETCHARFORMAT */
+#if 0
+      if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK))
+      {
+#endif
+        /* CFE_LINK must be unset from this range */
+        memset(&link, 0, sizeof(CHARFORMAT2W));
+        link.cbSize = sizeof(link);
+        link.dwMask = CFM_LINK;
+        link.dwEffects = 0;
+        ME_SetCharFormat(editor, beforeURL[0], beforeURL[1] - beforeURL[0], &link);
+        modified = TRUE;
+#if 0
+      }
+#endif
+    }
+    if (inURL[0] < inURL[1])
+    {
+      /* CFE_LINK effect should be consistently set */
+      link.cbSize = sizeof(link);
+      ME_GetCharFormat(editor, inURL[0], inURL[1], &link);
+      if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK))
+      {
+        /* CFE_LINK must be set on this range */
+        memset(&link, 0, sizeof(CHARFORMAT2W));
+        link.cbSize = sizeof(link);
+        link.dwMask = CFM_LINK;
+        link.dwEffects = CFE_LINK;
+        ME_SetCharFormat(editor, inURL[0], inURL[1] - inURL[0], &link);
+        modified = TRUE;
+      }
+    }
+  } while (sel_min < sel_max);
+  return modified;
+}
+
+void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor)
+{
+  ME_DisplayItem * startPara, * endPara;
+  ME_DisplayItem * item;
+  int dummy;
+  int from, to;
+
+  ME_GetSelection(editor, &from, &to);
+  if (from > to) from ^= to, to ^=from, from ^= to;
+  startPara = NULL; endPara = NULL;
+
+  /* Find paragraph previous to the one that contains start cursor */
+  item = ME_FindItemAtOffset(editor, diRun, from, &dummy);
+  if (item) {
+    startPara = ME_FindItemBack(item, diParagraph);
+    item = ME_FindItemBack(startPara, diParagraph);
+    if (item) startPara = item;
+  }
+
+  /* Find paragraph that contains end cursor */
+  item = ME_FindItemAtOffset(editor, diRun, to, &dummy);
+  if (item) {
+    endPara = ME_FindItemFwd(item, diParagraph);
+  }
+
+  if (startPara && endPara) {
+    ME_UpdateLinkAttribute(editor,
+      startPara->member.para.nCharOfs,
+      endPara->member.para.nCharOfs);
+  } else if (startPara) {
+    ME_UpdateLinkAttribute(editor,
+      startPara->member.para.nCharOfs,
+      -1);
+  }
+}
index 8bc819353ffbb90591e1f88690e27ada4ad5ac80..61ecf1b4098336851fc9dc0790c45d6a68547b97 100644 (file)
@@ -282,6 +282,8 @@ 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);
+BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, int sel_min, int sel_max);
+void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor);
 
 /* undo.c */
 ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, const ME_DisplayItem *pdi);
index 69951b8dc75051d7d1276f13008270095c27dd1d..05824bb5b3c9324b159984b6e0b387fb58e6ac60 100644 (file)
@@ -1040,6 +1040,7 @@ static void test_EM_AUTOURLDETECT(void)
     "This is some text with X\\ on it",
   };
   char buffer[1024];
+  MSG msg;
 
   parent = new_static_wnd(NULL);
   hwndRichEdit = new_richedit(parent);
@@ -1222,6 +1223,162 @@ static void test_EM_AUTOURLDETECT(void)
     hwndRichEdit = NULL;
   }
 
+#define INSERT_CR \
+  do { \
+    keybd_event('\r', 0x1c, 0, 0); \
+    keybd_event('\r', 0x1c, KEYEVENTF_KEYUP, 0); \
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
+      TranslateMessage(&msg); \
+      DispatchMessage(&msg); \
+    } \
+  } while (0)
+
+#define INSERT_BS \
+  do { \
+    keybd_event(0x08, 0x0e, 0, 0); \
+    keybd_event(0x08, 0x0e, KEYEVENTF_KEYUP, 0); \
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \
+      TranslateMessage(&msg); \
+      DispatchMessage(&msg); \
+    } \
+  } while (0)
+
+  /* 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') {
+          INSERT_CR;
+        } 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);
+      INSERT_CR;
+      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);
+      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);
+        }
+      }
+
+      /* The following will remove the just-inserted paragraph break, thus
+         restoring the URL */
+      SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
+      INSERT_BS;
+      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;
+  }
+
   DestroyWindow(parent);
 }