riched20: Implements FR_WHOLEWORD for EM_FINDTEXT.
Add FR_WHOLEWORD to riched20's EM_FINDTEXT and fix a couple small bugs introduced in earlier patches for EM_FINDTEXT. Additional test cases have been added to check for corner cases. Also improve input validation.
This commit is contained in:
parent
6bddeb1cf7
commit
b4ee2ad0e4
|
@ -764,34 +764,38 @@ ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int
|
|||
static int
|
||||
ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CHARRANGE *chrgText)
|
||||
{
|
||||
const int nLen = lstrlenW(text);
|
||||
const int nTextLen = ME_GetTextLength(editor);
|
||||
int nStart, nEnd;
|
||||
int nLen = lstrlenW(text);
|
||||
int nMin, nMax;
|
||||
ME_DisplayItem *item;
|
||||
ME_DisplayItem *para;
|
||||
WCHAR wLastChar = ' ';
|
||||
|
||||
TRACE("flags==0x%08lx, chrg->cpMin==%ld, chrg->cpMax==%ld text==%s\n",
|
||||
flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
|
||||
|
||||
if (flags & ~(FR_DOWN | FR_MATCHCASE))
|
||||
FIXME("Flags 0x%08lx not implemented\n", flags & ~(FR_DOWN | FR_MATCHCASE));
|
||||
if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD))
|
||||
FIXME("Flags 0x%08lx not implemented\n",
|
||||
flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD));
|
||||
|
||||
nMin = chrg->cpMin;
|
||||
if (chrg->cpMax == -1)
|
||||
nMax = ME_GetTextLength(editor);
|
||||
nMax = nTextLen;
|
||||
else
|
||||
nMax = chrg->cpMax;
|
||||
nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax;
|
||||
|
||||
/* when searching up, if cpMin < cpMax, then instead of searching
|
||||
* on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
|
||||
* [cpMax, cpMin]
|
||||
* [cpMax, cpMin]. The exception is when cpMax is -1, in which
|
||||
* case, it is always bigger than cpMin.
|
||||
*/
|
||||
if (!(flags & FR_DOWN))
|
||||
{
|
||||
int nSwap = nMax;
|
||||
|
||||
nMax = nMin;
|
||||
if (nMin < nSwap)
|
||||
nMax = nMin > nTextLen ? nTextLen : nMin;
|
||||
if (nMin < nSwap || chrg->cpMax == -1)
|
||||
nMin = 0;
|
||||
else
|
||||
nMin = nSwap;
|
||||
|
@ -806,6 +810,20 @@ ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CH
|
|||
|
||||
if (flags & FR_DOWN) /* Forward search */
|
||||
{
|
||||
/* If possible, find the character before where the search starts */
|
||||
if ((flags & FR_WHOLEWORD) && nMin)
|
||||
{
|
||||
nStart = nMin - 1;
|
||||
item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
|
||||
if (!item)
|
||||
{
|
||||
if (chrgText)
|
||||
chrgText->cpMin = chrgText->cpMax = -1;
|
||||
return -1;
|
||||
}
|
||||
wLastChar = item->member.run.strText->szData[nStart];
|
||||
}
|
||||
|
||||
nStart = nMin;
|
||||
item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
|
||||
if (!item)
|
||||
|
@ -825,10 +843,35 @@ ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CH
|
|||
|
||||
while (pCurItem && ME_CharCompare(pCurItem->member.run.strText->szData[nCurStart + nMatched], text[nMatched], (flags & FR_MATCHCASE)))
|
||||
{
|
||||
if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
|
||||
break;
|
||||
|
||||
nMatched++;
|
||||
if (nMatched == nLen)
|
||||
{
|
||||
nStart += para->member.para.nCharOfs + item->member.run.nCharOfs;
|
||||
ME_DisplayItem *pNextItem = pCurItem;
|
||||
int nNextStart = nCurStart;
|
||||
WCHAR wNextChar;
|
||||
|
||||
/* Check to see if next character is a whitespace */
|
||||
if (flags & FR_WHOLEWORD)
|
||||
{
|
||||
if (nCurStart + nMatched == ME_StrLen(pCurItem->member.run.strText))
|
||||
{
|
||||
pNextItem = ME_FindItemFwd(pCurItem, diRun);
|
||||
nNextStart = -nMatched;
|
||||
}
|
||||
|
||||
if (pNextItem)
|
||||
wNextChar = pNextItem->member.run.strText->szData[nNextStart + nMatched];
|
||||
else
|
||||
wNextChar = ' ';
|
||||
|
||||
if (isalnumW(wNextChar))
|
||||
break;
|
||||
}
|
||||
|
||||
nStart += para->member.para.nCharOfs + pCurItem->member.run.nCharOfs;
|
||||
if (chrgText)
|
||||
{
|
||||
chrgText->cpMin = nStart;
|
||||
|
@ -844,6 +887,11 @@ ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CH
|
|||
nCurStart = -nMatched;
|
||||
}
|
||||
}
|
||||
if (pCurItem)
|
||||
wLastChar = pCurItem->member.run.strText->szData[nCurStart + nMatched];
|
||||
else
|
||||
wLastChar = ' ';
|
||||
|
||||
nStart++;
|
||||
if (nStart == ME_StrLen(item->member.run.strText))
|
||||
{
|
||||
|
@ -855,9 +903,24 @@ ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CH
|
|||
}
|
||||
else /* Backward search */
|
||||
{
|
||||
/* If possible, find the character after where the search ends */
|
||||
if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
|
||||
{
|
||||
nEnd = nMax + 1;
|
||||
item = ME_FindItemAtOffset(editor, diRun, nEnd, &nEnd);
|
||||
if (!item)
|
||||
{
|
||||
if (chrgText)
|
||||
chrgText->cpMin = chrgText->cpMax = -1;
|
||||
return -1;
|
||||
}
|
||||
wLastChar = item->member.run.strText->szData[nEnd];
|
||||
}
|
||||
|
||||
nEnd = nMax;
|
||||
item = ME_FindItemAtOffset(editor, diRun, nEnd, &nEnd);
|
||||
if (!item) {
|
||||
if (!item)
|
||||
{
|
||||
if (chrgText)
|
||||
chrgText->cpMin = chrgText->cpMax = -1;
|
||||
return -1;
|
||||
|
@ -872,12 +935,45 @@ ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CH
|
|||
int nCurEnd = nEnd;
|
||||
int nMatched = 0;
|
||||
|
||||
while (ME_CharCompare(pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1], text[nLen - nMatched - 1], (flags & FR_MATCHCASE)))
|
||||
if (nCurEnd - nMatched == 0)
|
||||
{
|
||||
pCurItem = ME_FindItemBack(pCurItem, diRun);
|
||||
para = ME_GetParagraph(pCurItem);
|
||||
nCurEnd = ME_StrLen(pCurItem->member.run.strText) + nMatched;
|
||||
}
|
||||
|
||||
while (pCurItem && ME_CharCompare(pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1], text[nLen - nMatched - 1], (flags & FR_MATCHCASE)))
|
||||
{
|
||||
if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
|
||||
break;
|
||||
|
||||
nMatched++;
|
||||
if (nMatched == nLen)
|
||||
{
|
||||
nStart = para->member.para.nCharOfs + item->member.run.nCharOfs + nCurEnd - nMatched;
|
||||
ME_DisplayItem *pPrevItem = pCurItem;
|
||||
int nPrevEnd = nCurEnd;
|
||||
WCHAR wPrevChar;
|
||||
|
||||
/* Check to see if previous character is a whitespace */
|
||||
if (flags & FR_WHOLEWORD)
|
||||
{
|
||||
if (nPrevEnd - nMatched == 0)
|
||||
{
|
||||
pPrevItem = ME_FindItemBack(pCurItem, diRun);
|
||||
if (pPrevItem)
|
||||
nPrevEnd = ME_StrLen(pPrevItem->member.run.strText) + nMatched;
|
||||
}
|
||||
|
||||
if (pPrevItem)
|
||||
wPrevChar = pPrevItem->member.run.strText->szData[nPrevEnd - nMatched - 1];
|
||||
else
|
||||
wPrevChar = ' ';
|
||||
|
||||
if (isalnumW(wPrevChar))
|
||||
break;
|
||||
}
|
||||
|
||||
nStart = para->member.para.nCharOfs + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
|
||||
if (chrgText)
|
||||
{
|
||||
chrgText->cpMin = nStart;
|
||||
|
@ -895,6 +991,11 @@ ME_FindText(ME_TextEditor *editor, DWORD flags, CHARRANGE *chrg, WCHAR *text, CH
|
|||
nCurEnd = ME_StrLen(pCurItem->member.run.strText) + nMatched;
|
||||
}
|
||||
}
|
||||
if (pCurItem)
|
||||
wLastChar = pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1];
|
||||
else
|
||||
wLastChar = ' ';
|
||||
|
||||
nEnd--;
|
||||
if (nEnd < 0)
|
||||
{
|
||||
|
|
|
@ -92,10 +92,16 @@ struct find_s find_tests2[] = {
|
|||
{10, 5, "", 0, -1, 0},
|
||||
|
||||
/* Whole-word search */
|
||||
{0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 1},
|
||||
{0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 1},
|
||||
{0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
|
||||
{0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
|
||||
{13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
|
||||
{0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
|
||||
{10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
|
||||
{11, -1, "winewine", FR_WHOLEWORD, 0, 0},
|
||||
{31, -1, "winewine", FR_WHOLEWORD, 23, 0},
|
||||
|
||||
/* Bad ranges */
|
||||
{5, 200, "XXX", FR_DOWN, -1, 0},
|
||||
{-20, 20, "Wine", FR_DOWN, -1, 0},
|
||||
{-20, 20, "Wine", FR_DOWN, -1, 0},
|
||||
{-15, -20, "Wine", FR_DOWN, -1, 0},
|
||||
|
|
Loading…
Reference in New Issue