From 4a5d1000978ee8e976bee7e93c5ece8114cf7233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Villac=C3=ADs=20Lasso?= Date: Wed, 18 Jun 2008 11:05:52 -0500 Subject: [PATCH] richedit: Add more tests for URL autodetection on WM_CHAR, make them pass under Wine. --- dlls/riched20/editor.c | 147 ++++++++++++++++++++++++++++---- dlls/riched20/editor.h | 2 + dlls/riched20/tests/editor.c | 157 +++++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+), 16 deletions(-) diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c index edd6be6da88..fc6e4eac579 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -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); + } +} diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index 8bc819353ff..61ecf1b4098 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -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); diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c index 69951b8dc75..05824bb5b3c 100644 --- a/dlls/riched20/tests/editor.c +++ b/dlls/riched20/tests/editor.c @@ -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); }