tdf#128009: Allow spaces in AutoText suggestions
Currently autotext entries with long names starting with spaces, or containing spaces after first or second character, would never be suggested when SvxAutoCorrCfg::IsAutoTextTip() gives true (set in Tools -> AutoCorrect -> [x] Display remainder of name as suggestion while typing), because only a single word no less than 3 chars long left to cursor is considered a candidate for the name matching. This change allows to consider multiple chunks of text left to the cursor as the candidates for name matching. The chunks are 3-9 characters long, may start only between words, and have spaces, including leading. Thus, AutoText entries with long names like " Dr Foo" will now be suggested for an entry like "lorem dr f". Change-Id: If91c957341a4f4b281acb0e4ada558706ea2f8c1 Reviewed-on: https://gerrit.libreoffice.org/80392 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
parent
cf1b5ae4c5
commit
ef2ec07b41
@ -1548,12 +1548,12 @@ bool SvxAutoCorrect::AddWrtSttException( const OUString& rNew,
|
||||
return pLists && pLists->AddToWrdSttExceptList(rNew);
|
||||
}
|
||||
|
||||
bool SvxAutoCorrect::GetPrevAutoCorrWord( SvxAutoCorrDoc const & rDoc,
|
||||
const OUString& rTxt, sal_Int32 nPos,
|
||||
OUString& rWord )
|
||||
OUString SvxAutoCorrect::GetPrevAutoCorrWord(SvxAutoCorrDoc const& rDoc, const OUString& rTxt,
|
||||
sal_Int32 nPos)
|
||||
{
|
||||
OUString sRet;
|
||||
if( !nPos )
|
||||
return false;
|
||||
return sRet;
|
||||
|
||||
sal_Int32 nEnde = nPos;
|
||||
|
||||
@ -1561,7 +1561,7 @@ bool SvxAutoCorrect::GetPrevAutoCorrWord( SvxAutoCorrDoc const & rDoc,
|
||||
if( ( nPos < rTxt.getLength() &&
|
||||
!IsWordDelim( rTxt[ nPos ])) ||
|
||||
IsWordDelim( rTxt[ --nPos ]))
|
||||
return false;
|
||||
return sRet;
|
||||
|
||||
while( nPos && !IsWordDelim( rTxt[ --nPos ]))
|
||||
;
|
||||
@ -1574,20 +1574,54 @@ bool SvxAutoCorrect::GetPrevAutoCorrWord( SvxAutoCorrDoc const & rDoc,
|
||||
|
||||
while( lcl_IsInAsciiArr( sImplSttSkipChars, rTxt[ nCapLttrPos ]) )
|
||||
if( ++nCapLttrPos >= nEnde )
|
||||
return false;
|
||||
return sRet;
|
||||
|
||||
if( 3 > nEnde - nCapLttrPos )
|
||||
return false;
|
||||
return sRet;
|
||||
|
||||
const LanguageType eLang = GetDocLanguage( rDoc, nCapLttrPos );
|
||||
|
||||
CharClass& rCC = GetCharClass(eLang);
|
||||
|
||||
if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nEnde ))
|
||||
return false;
|
||||
return sRet;
|
||||
|
||||
rWord = rTxt.copy( nCapLttrPos, nEnde - nCapLttrPos );
|
||||
return true;
|
||||
sRet = rTxt.copy( nCapLttrPos, nEnde - nCapLttrPos );
|
||||
return sRet;
|
||||
}
|
||||
|
||||
// static
|
||||
std::vector<OUString> SvxAutoCorrect::GetChunkForAutoText(const OUString& rTxt,
|
||||
const sal_Int32 nPos)
|
||||
{
|
||||
constexpr sal_Int32 nMinLen = 3;
|
||||
constexpr sal_Int32 nMaxLen = 9;
|
||||
std::vector<OUString> aRes;
|
||||
if (nPos >= nMinLen)
|
||||
{
|
||||
sal_Int32 nBegin = std::max<sal_Int32>(nPos - nMaxLen, 0);
|
||||
// TODO: better detect word boundaries (not only whitespaces, but also e.g. punctuation)
|
||||
if (nBegin > 0 && !IsWordDelim(rTxt[nBegin-1]))
|
||||
{
|
||||
while (nBegin + nMinLen <= nPos && !IsWordDelim(rTxt[nBegin]))
|
||||
++nBegin;
|
||||
}
|
||||
if (nBegin + nMinLen <= nPos)
|
||||
{
|
||||
OUString sRes = rTxt.copy(nBegin, nPos - nBegin);
|
||||
aRes.push_back(sRes);
|
||||
bool bLastStartedWithDelim = IsWordDelim(sRes[0]);
|
||||
for (sal_Int32 i = 1; i <= sRes.getLength() - nMinLen; ++i)
|
||||
{
|
||||
bool bAdd = bLastStartedWithDelim;
|
||||
bLastStartedWithDelim = IsWordDelim(sRes[i]);
|
||||
bAdd = bAdd || bLastStartedWithDelim;
|
||||
if (bAdd)
|
||||
aRes.push_back(sRes.copy(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return aRes;
|
||||
}
|
||||
|
||||
bool SvxAutoCorrect::CreateLanguageFile( const LanguageTag& rLanguageTag, bool bNewFile )
|
||||
|
@ -291,8 +291,13 @@ public:
|
||||
|
||||
// Return for the autotext expansion the previous word,
|
||||
// AutoCorrect - corresponding algorithm
|
||||
bool GetPrevAutoCorrWord( SvxAutoCorrDoc const & rDoc, const OUString& rTxt,
|
||||
sal_Int32 nPos, OUString& rWord );
|
||||
OUString GetPrevAutoCorrWord(SvxAutoCorrDoc const& rDoc, const OUString& rTxt, sal_Int32 nPos);
|
||||
|
||||
// Returns vector candidates for AutoText name match, starting with the longest string between
|
||||
// 3 and 9 characters long, that is a chunk of text starting with a whitespace or with a word's
|
||||
// first character, and ending at the current cursor position or empty string if no such string
|
||||
// exists
|
||||
static std::vector<OUString> GetChunkForAutoText(const OUString& rTxt, sal_Int32 nPos);
|
||||
|
||||
// Search for the words in the replacement table.
|
||||
// rText - check in this text the words of the list
|
||||
@ -331,6 +336,7 @@ public:
|
||||
// Query/Set the current settings of AutoCorrect
|
||||
ACFlags GetFlags() const { return nFlags; }
|
||||
SvxSwAutoFormatFlags& GetSwFlags() { return aSwFlags;}
|
||||
const SvxSwAutoFormatFlags& GetSwFlags() const { return aSwFlags; }
|
||||
bool IsAutoCorrFlag( ACFlags nFlag ) const
|
||||
{ return bool(nFlags & nFlag); }
|
||||
void SetAutoCorrFlag( ACFlags nFlag, bool bOn = true );
|
||||
|
@ -816,7 +816,11 @@ public:
|
||||
/// Call AutoCorrect
|
||||
void AutoCorrect( SvxAutoCorrect& rACorr, bool bInsertMode,
|
||||
sal_Unicode cChar );
|
||||
bool GetPrevAutoCorrWord(SvxAutoCorrect& rACorr, OUString& rWord);
|
||||
OUString GetPrevAutoCorrWord(SvxAutoCorrect& rACorr);
|
||||
|
||||
// We consider no more than 9 characters before the cursor, and they must not start in the
|
||||
// middle of a word (leading spaces are OK)
|
||||
std::vector<OUString> GetChunkForAutoText();
|
||||
|
||||
/// Set our styles according to the respective rules.
|
||||
void AutoFormat( const SvxSwAutoFormatFlags* pAFlags );
|
||||
|
@ -279,11 +279,11 @@ void SwEditShell::SetNewDoc()
|
||||
GetDoc()->getIDocumentState().SetNewDoc(true);
|
||||
}
|
||||
|
||||
bool SwEditShell::GetPrevAutoCorrWord(SvxAutoCorrect& rACorr, OUString& rWord)
|
||||
OUString SwEditShell::GetPrevAutoCorrWord(SvxAutoCorrect& rACorr)
|
||||
{
|
||||
SET_CURR_SHELL( this );
|
||||
|
||||
bool bRet;
|
||||
OUString sRet;
|
||||
SwPaM* pCursor = getShellCursor( true );
|
||||
SwTextNode* pTNd = pCursor->GetNode().GetTextNode();
|
||||
if (pTNd)
|
||||
@ -291,12 +291,25 @@ bool SwEditShell::GetPrevAutoCorrWord(SvxAutoCorrect& rACorr, OUString& rWord)
|
||||
SwAutoCorrDoc aSwAutoCorrDoc( *this, *pCursor, 0 );
|
||||
SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(GetLayout())));
|
||||
TextFrameIndex const nPos(pFrame->MapModelToViewPos(*pCursor->GetPoint()));
|
||||
bRet = rACorr.GetPrevAutoCorrWord( aSwAutoCorrDoc,
|
||||
pFrame->GetText(), sal_Int32(nPos), rWord );
|
||||
sRet = rACorr.GetPrevAutoCorrWord(aSwAutoCorrDoc, pFrame->GetText(), sal_Int32(nPos));
|
||||
}
|
||||
else
|
||||
bRet = false;
|
||||
return bRet;
|
||||
return sRet;
|
||||
}
|
||||
|
||||
std::vector<OUString> SwEditShell::GetChunkForAutoText()
|
||||
{
|
||||
SET_CURR_SHELL(this);
|
||||
|
||||
std::vector<OUString> aRet;
|
||||
SwPaM* pCursor = getShellCursor(true);
|
||||
SwTextNode* pTNd = pCursor->GetNode().GetTextNode();
|
||||
if (pTNd)
|
||||
{
|
||||
const auto pFrame = static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(GetLayout()));
|
||||
TextFrameIndex const nPos(pFrame->MapModelToViewPos(*pCursor->GetPoint()));
|
||||
aRet = SvxAutoCorrect::GetChunkForAutoText(pFrame->GetText(), sal_Int32(nPos));
|
||||
}
|
||||
return aRet;
|
||||
}
|
||||
|
||||
SwAutoCompleteWord& SwEditShell::GetAutoCompleteWords()
|
||||
|
@ -264,12 +264,11 @@ public:
|
||||
/// Assists with auto-completion of AutoComplete words and AutoText names.
|
||||
struct QuickHelpData
|
||||
{
|
||||
/// Strings that at least partially match an input word.
|
||||
std::vector<OUString> m_aHelpStrings;
|
||||
/// Strings that at least partially match an input word, and match length.
|
||||
std::vector<std::pair<OUString, sal_uInt16>> m_aHelpStrings;
|
||||
/// Index of the current help string.
|
||||
sal_uInt16 nCurArrPos;
|
||||
/// Length of the input word associated with the help data.
|
||||
sal_uInt16 nLen;
|
||||
static constexpr sal_uInt16 nNoPos = std::numeric_limits<sal_uInt16>::max();
|
||||
|
||||
/// Help data stores AutoText names rather than AutoComplete words.
|
||||
bool m_bIsAutoText;
|
||||
@ -287,10 +286,12 @@ struct QuickHelpData
|
||||
|
||||
void Move( QuickHelpData& rCpy );
|
||||
void ClearContent();
|
||||
void Start( SwWrtShell& rSh, sal_uInt16 nWrdLen );
|
||||
void Start(SwWrtShell& rSh, bool bRestart);
|
||||
void Stop( SwWrtShell& rSh );
|
||||
|
||||
bool HasContent() const { return !m_aHelpStrings.empty() && 0 != nLen; }
|
||||
bool HasContent() const { return !m_aHelpStrings.empty() && nCurArrPos != nNoPos; }
|
||||
const OUString& CurStr() const { return m_aHelpStrings[nCurArrPos].first; }
|
||||
sal_uInt16 CurLen() const { return m_aHelpStrings[nCurArrPos].second; }
|
||||
|
||||
/// Next help string.
|
||||
void Next( bool bEndLess )
|
||||
@ -2563,7 +2564,7 @@ KEYINPUT_CHECKTABLE_INSDEL:
|
||||
// replace the word or abbreviation with the auto text
|
||||
rSh.StartUndo( SwUndoId::START );
|
||||
|
||||
OUString sFnd( aTmpQHD.m_aHelpStrings[ aTmpQHD.nCurArrPos ] );
|
||||
OUString sFnd(aTmpQHD.CurStr());
|
||||
if( aTmpQHD.m_bIsAutoText )
|
||||
{
|
||||
SwGlossaryList* pList = ::GetGlossaryList();
|
||||
@ -2572,7 +2573,7 @@ KEYINPUT_CHECKTABLE_INSDEL:
|
||||
if(pList->GetShortName( sFnd, sShrtNm, sGroup))
|
||||
{
|
||||
rSh.SttSelect();
|
||||
rSh.ExtendSelection( false, aTmpQHD.nLen );
|
||||
rSh.ExtendSelection(false, aTmpQHD.CurLen());
|
||||
SwGlossaryHdl* pGlosHdl = GetView().GetGlosHdl();
|
||||
pGlosHdl->SetCurGroup(sGroup, true);
|
||||
pGlosHdl->InsertGlossary( sShrtNm);
|
||||
@ -2581,7 +2582,7 @@ KEYINPUT_CHECKTABLE_INSDEL:
|
||||
}
|
||||
else
|
||||
{
|
||||
sFnd = sFnd.copy( aTmpQHD.nLen );
|
||||
sFnd = sFnd.copy(aTmpQHD.CurLen());
|
||||
rSh.Insert( sFnd );
|
||||
m_pQuickHlpData->m_bAppendSpace = !pACorr ||
|
||||
pACorr->GetSwFlags().bAutoCmpltAppendBlanc;
|
||||
@ -2592,7 +2593,7 @@ KEYINPUT_CHECKTABLE_INSDEL:
|
||||
|
||||
case SwKeyState::NextPrevGlossary:
|
||||
m_pQuickHlpData->Move( aTmpQHD );
|
||||
m_pQuickHlpData->Start( rSh, USHRT_MAX );
|
||||
m_pQuickHlpData->Start(rSh, false);
|
||||
break;
|
||||
|
||||
case SwKeyState::EditFormula:
|
||||
@ -2664,13 +2665,12 @@ KEYINPUT_CHECKTABLE_INSDEL:
|
||||
g_bFlushCharBuffer = bSave;
|
||||
|
||||
// maybe show Tip-Help
|
||||
OUString sWord;
|
||||
if( bNormalChar && pACfg && pACorr &&
|
||||
( pACfg->IsAutoTextTip() ||
|
||||
pACorr->GetSwFlags().bAutoCompleteWords ) &&
|
||||
rSh.GetPrevAutoCorrWord( *pACorr, sWord ) )
|
||||
if (bNormalChar)
|
||||
{
|
||||
ShowAutoTextCorrectQuickHelp(sWord, pACfg, pACorr);
|
||||
const bool bAutoTextShown
|
||||
= pACfg->IsAutoTextTip() && ShowAutoText(rSh.GetChunkForAutoText());
|
||||
if (!bAutoTextShown && pACorr && pACorr->GetSwFlags().bAutoCompleteWords)
|
||||
ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5393,18 +5393,13 @@ void SwEditWin::Command( const CommandEvent& rCEvt )
|
||||
m_rView.GetViewFrame()->GetBindings().GetRecorder();
|
||||
if(!xRecorder.is())
|
||||
{
|
||||
SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get();
|
||||
SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get();
|
||||
if (!rACfg.IsAutoTextTip() || !ShowAutoText(rSh.GetChunkForAutoText()))
|
||||
{
|
||||
SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect();
|
||||
if( pACorr &&
|
||||
// If autocompletion required...
|
||||
( rACfg.IsAutoTextTip() ||
|
||||
pACorr->GetSwFlags().bAutoCompleteWords ) &&
|
||||
// ... and extraction of last word from text input was successful...
|
||||
rSh.GetPrevAutoCorrWord( *pACorr, sWord ) )
|
||||
{
|
||||
// ... request for auto completion help to be shown.
|
||||
ShowAutoTextCorrectQuickHelp(sWord, &rACfg, pACorr, true);
|
||||
}
|
||||
if (pACorr && pACorr->GetSwFlags().bAutoCompleteWords)
|
||||
ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5870,7 +5865,6 @@ void QuickHelpData::Move( QuickHelpData& rCpy )
|
||||
m_aHelpStrings.swap( rCpy.m_aHelpStrings );
|
||||
|
||||
m_bIsDisplayed = rCpy.m_bIsDisplayed;
|
||||
nLen = rCpy.nLen;
|
||||
nCurArrPos = rCpy.nCurArrPos;
|
||||
m_bAppendSpace = rCpy.m_bAppendSpace;
|
||||
m_bIsTip = rCpy.m_bIsTip;
|
||||
@ -5879,7 +5873,7 @@ void QuickHelpData::Move( QuickHelpData& rCpy )
|
||||
|
||||
void QuickHelpData::ClearContent()
|
||||
{
|
||||
nLen = nCurArrPos = 0;
|
||||
nCurArrPos = nNoPos;
|
||||
m_bIsDisplayed = m_bAppendSpace = false;
|
||||
nTipId = nullptr;
|
||||
m_aHelpStrings.clear();
|
||||
@ -5887,11 +5881,10 @@ void QuickHelpData::ClearContent()
|
||||
m_bIsAutoText = true;
|
||||
}
|
||||
|
||||
void QuickHelpData::Start( SwWrtShell& rSh, sal_uInt16 nWrdLen )
|
||||
void QuickHelpData::Start(SwWrtShell& rSh, const bool bRestart)
|
||||
{
|
||||
if( USHRT_MAX != nWrdLen )
|
||||
if (bRestart)
|
||||
{
|
||||
nLen = nWrdLen;
|
||||
nCurArrPos = 0;
|
||||
}
|
||||
m_bIsDisplayed = true;
|
||||
@ -5903,13 +5896,13 @@ void QuickHelpData::Start( SwWrtShell& rSh, sal_uInt16 nWrdLen )
|
||||
rSh.GetCharRect().Pos() )));
|
||||
aPt.AdjustY( -3 );
|
||||
nTipId = Help::ShowPopover(&rWin, tools::Rectangle( aPt, Size( 1, 1 )),
|
||||
m_aHelpStrings[ nCurArrPos ],
|
||||
CurStr(),
|
||||
QuickHelpFlags::Left | QuickHelpFlags::Bottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
OUString sStr( m_aHelpStrings[ nCurArrPos ] );
|
||||
sStr = sStr.copy( nLen );
|
||||
OUString sStr(CurStr());
|
||||
sStr = sStr.copy(CurLen());
|
||||
sal_uInt16 nL = sStr.getLength();
|
||||
const ExtTextInputAttr nVal = ExtTextInputAttr::DottedUnderline |
|
||||
ExtTextInputAttr::Highlight;
|
||||
@ -5986,23 +5979,24 @@ void QuickHelpData::FillStrArr( SwWrtShell const & rSh, const OUString& rWord )
|
||||
if( rStr.getLength() > rWord.getLength() &&
|
||||
rCC.lowercase( rStr, 0, rWord.getLength() ) == sWordLower )
|
||||
{
|
||||
OUString sStr;
|
||||
|
||||
//fdo#61251 if it's an exact match, ensure unchanged replacement
|
||||
//exists as a candidate
|
||||
if (rStr.startsWith(rWord))
|
||||
m_aHelpStrings.push_back(rStr);
|
||||
m_aHelpStrings.emplace_back(rStr, rWord.getLength());
|
||||
else
|
||||
sStr = rStr; // to be added if no case conversion is performed below
|
||||
|
||||
if ( aWordCase == CASE_LOWER )
|
||||
m_aHelpStrings.push_back( rCC.lowercase( rStr ) );
|
||||
sStr = rCC.lowercase(rStr);
|
||||
else if ( aWordCase == CASE_SENTENCE )
|
||||
{
|
||||
OUString sTmp = rCC.lowercase( rStr );
|
||||
sTmp = sTmp.replaceAt( 0, 1, OUString(rStr[0]) );
|
||||
m_aHelpStrings.push_back( sTmp );
|
||||
}
|
||||
sStr = rCC.lowercase(rStr).replaceAt(0, 1, OUString(rStr[0]));
|
||||
else if ( aWordCase == CASE_UPPER )
|
||||
m_aHelpStrings.push_back( rCC.uppercase( rStr ) );
|
||||
else // CASE_OTHER - use retrieved capitalization
|
||||
m_aHelpStrings.push_back( rStr );
|
||||
sStr = rCC.uppercase(rStr);
|
||||
|
||||
if (!sStr.isEmpty())
|
||||
m_aHelpStrings.emplace_back(sStr, rWord.getLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6027,7 +6021,7 @@ void QuickHelpData::FillStrArr( SwWrtShell const & rSh, const OUString& rWord )
|
||||
// only for "201" or "2016-..." (to avoid unintentional text
|
||||
// insertion at line ending, for example typing "30 January 2016")
|
||||
if (rWord.getLength() != 4 && rStrToday.startsWith(rWord))
|
||||
m_aHelpStrings.push_back(rStrToday);
|
||||
m_aHelpStrings.emplace_back(rStrToday, rWord.getLength());
|
||||
}
|
||||
|
||||
// Add matching words from AutoCompleteWord list
|
||||
@ -6044,22 +6038,25 @@ void QuickHelpData::FillStrArr( SwWrtShell const & rSh, const OUString& rWord )
|
||||
if (!rStrToday.isEmpty() && aCompletedString.startsWith(rWord))
|
||||
continue;
|
||||
|
||||
OUString sStr;
|
||||
|
||||
//fdo#61251 if it's an exact match, ensure unchanged replacement
|
||||
//exists as a candidate
|
||||
if (aCompletedString.startsWith(rWord))
|
||||
m_aHelpStrings.push_back(aCompletedString);
|
||||
if ( aWordCase == CASE_LOWER )
|
||||
m_aHelpStrings.push_back( rCC.lowercase( aCompletedString ) );
|
||||
else if ( aWordCase == CASE_SENTENCE )
|
||||
{
|
||||
OUString sTmp = rCC.lowercase( aCompletedString );
|
||||
sTmp = sTmp.replaceAt( 0, 1, OUString(aCompletedString[0]) );
|
||||
m_aHelpStrings.push_back( sTmp );
|
||||
}
|
||||
else if ( aWordCase == CASE_UPPER )
|
||||
m_aHelpStrings.push_back( rCC.uppercase( aCompletedString ) );
|
||||
else // CASE_OTHER - use retrieved capitalization
|
||||
m_aHelpStrings.push_back( aCompletedString );
|
||||
m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength());
|
||||
else
|
||||
sStr = aCompletedString; // to be added if no case conversion is performed below
|
||||
|
||||
if (aWordCase == CASE_LOWER)
|
||||
sStr = rCC.lowercase(aCompletedString);
|
||||
else if (aWordCase == CASE_SENTENCE)
|
||||
sStr = rCC.lowercase(aCompletedString)
|
||||
.replaceAt(0, 1, OUString(aCompletedString[0]));
|
||||
else if (aWordCase == CASE_UPPER)
|
||||
sStr = rCC.uppercase(aCompletedString);
|
||||
|
||||
if (!sStr.isEmpty())
|
||||
m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6075,15 +6072,16 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const OUString& s1, const OUString& s2) const
|
||||
bool operator()(const std::pair<OUString, sal_uInt16>& s1,
|
||||
const std::pair<OUString, sal_uInt16>& s2) const
|
||||
{
|
||||
int nRet = s1.compareToIgnoreAsciiCase(s2);
|
||||
int nRet = s1.first.compareToIgnoreAsciiCase(s2.first);
|
||||
if (nRet == 0)
|
||||
{
|
||||
//fdo#61251 sort stuff that starts with the exact rOrigWord before
|
||||
//another ignore-case candidate
|
||||
int n1StartsWithOrig = s1.startsWith(m_rOrigWord) ? 0 : 1;
|
||||
int n2StartsWithOrig = s2.startsWith(m_rOrigWord) ? 0 : 1;
|
||||
int n1StartsWithOrig = s1.first.startsWith(m_rOrigWord) ? 0 : 1;
|
||||
int n2StartsWithOrig = s2.first.startsWith(m_rOrigWord) ? 0 : 1;
|
||||
return n1StartsWithOrig < n2StartsWithOrig;
|
||||
}
|
||||
return nRet < 0;
|
||||
@ -6092,9 +6090,10 @@ public:
|
||||
|
||||
struct EqualIgnoreCaseAscii
|
||||
{
|
||||
bool operator()(const OUString& s1, const OUString& s2) const
|
||||
bool operator()(const std::pair<OUString, sal_uInt16>& s1,
|
||||
const std::pair<OUString, sal_uInt16>& s2) const
|
||||
{
|
||||
return s1.equalsIgnoreAsciiCase(s2);
|
||||
return s1.first.equalsIgnoreAsciiCase(s2.first);
|
||||
}
|
||||
};
|
||||
|
||||
@ -6107,33 +6106,74 @@ void QuickHelpData::SortAndFilter(const OUString &rOrigWord)
|
||||
m_aHelpStrings.end(),
|
||||
CompareIgnoreCaseAsciiFavorExact(rOrigWord) );
|
||||
|
||||
std::vector<OUString>::iterator it = std::unique( m_aHelpStrings.begin(),
|
||||
m_aHelpStrings.end(),
|
||||
EqualIgnoreCaseAscii() );
|
||||
const auto& it
|
||||
= std::unique(m_aHelpStrings.begin(), m_aHelpStrings.end(), EqualIgnoreCaseAscii());
|
||||
m_aHelpStrings.erase( it, m_aHelpStrings.end() );
|
||||
|
||||
nCurArrPos = 0;
|
||||
}
|
||||
|
||||
void SwEditWin::ShowAutoTextCorrectQuickHelp(
|
||||
const OUString& rWord, SvxAutoCorrCfg const * pACfg, SvxAutoCorrect* pACorr,
|
||||
bool bFromIME )
|
||||
// For a given chunk of typed text between 3 and 9 characters long that may start at a word boundary
|
||||
// or in a whitespace and may include whitespaces, SwEditShell::GetChunkForAutoTextcreates a list of
|
||||
// possible candidates for long AutoText names. Let's say, we have typed text "lorem ipsum dr f";
|
||||
// and the cursor is right after the "f". SwEditShell::GetChunkForAutoText would take " dr f",
|
||||
// since it's the longest chunk to the left of the cursor no longer than 9 characters, not starting
|
||||
// in the middle of a word. Then it would create this list from it (in this order, longest first):
|
||||
// " dr f"
|
||||
// " dr f"
|
||||
// "dr f"
|
||||
// It cannot add "r f", because it starts in the middle of the word "dr"; also it cannot give " f",
|
||||
// because it's only 2 characters long.
|
||||
// Now the result of SwEditShell::GetChunkForAutoText is passed here to SwEditWin::ShowAutoText, and
|
||||
// then to SwGlossaryList::HasLongName, where all existing autotext entries' long names are tested
|
||||
// if they start with one of the list elements. The matches are sorted according the position of the
|
||||
// candidate that matched first, then alhpabetically inside the group of suggestions for a given
|
||||
// candidate. Say, if we have these AutoText entry long names:
|
||||
// "Dr Frodo"
|
||||
// "Dr Credo"
|
||||
// "Or Bilbo"
|
||||
// "dr foo"
|
||||
// " Dr Fuzz"
|
||||
// " dr Faust"
|
||||
// the resulting list would be:
|
||||
// " Dr Fuzz" -> matches the first (longest) item in the candidates list
|
||||
// " dr Faust" -> matches the second candidate item
|
||||
// "Dr Foo" -> first item of the two matching the third candidate; alphabetically sorted
|
||||
// "Dr Frodo" -> second item of the two matching the third candidate; alphabetically sorted
|
||||
// Each of the resulting suggestions knows the length of the candidate it replaces, so accepting the
|
||||
// first suggestion would replace 6 characters before cursor, while tabbing to and accepting the
|
||||
// last suggestion would replace only 4 characters to the left of cursor.
|
||||
bool SwEditWin::ShowAutoText(const std::vector<OUString>& rChunkCandidates)
|
||||
{
|
||||
SwWrtShell& rSh = m_rView.GetWrtShell();
|
||||
m_pQuickHlpData->ClearContent();
|
||||
if( pACfg->IsAutoTextTip() )
|
||||
if (!rChunkCandidates.empty())
|
||||
{
|
||||
SwGlossaryList* pList = ::GetGlossaryList();
|
||||
pList->HasLongName( rWord, &m_pQuickHlpData->m_aHelpStrings );
|
||||
pList->HasLongName(rChunkCandidates, m_pQuickHlpData->m_aHelpStrings);
|
||||
}
|
||||
|
||||
if (!m_pQuickHlpData->m_aHelpStrings.empty())
|
||||
{
|
||||
m_pQuickHlpData->Start(m_rView.GetWrtShell(), true);
|
||||
}
|
||||
return !m_pQuickHlpData->m_aHelpStrings.empty();
|
||||
}
|
||||
|
||||
void SwEditWin::ShowAutoCorrectQuickHelp(
|
||||
const OUString& rWord, SvxAutoCorrect& rACorr,
|
||||
bool bFromIME )
|
||||
{
|
||||
if (rWord.isEmpty())
|
||||
return;
|
||||
SwWrtShell& rSh = m_rView.GetWrtShell();
|
||||
m_pQuickHlpData->ClearContent();
|
||||
|
||||
if( m_pQuickHlpData->m_aHelpStrings.empty() &&
|
||||
pACorr->GetSwFlags().bAutoCompleteWords )
|
||||
rACorr.GetSwFlags().bAutoCompleteWords )
|
||||
{
|
||||
m_pQuickHlpData->m_bIsAutoText = false;
|
||||
m_pQuickHlpData->m_bIsTip = bFromIME ||
|
||||
!pACorr ||
|
||||
pACorr->GetSwFlags().bAutoCmpltShowAsTip;
|
||||
rACorr.GetSwFlags().bAutoCmpltShowAsTip;
|
||||
|
||||
// Get the necessary data to show help text.
|
||||
m_pQuickHlpData->FillStrArr( rSh, rWord );
|
||||
@ -6142,7 +6182,7 @@ void SwEditWin::ShowAutoTextCorrectQuickHelp(
|
||||
if( !m_pQuickHlpData->m_aHelpStrings.empty() )
|
||||
{
|
||||
m_pQuickHlpData->SortAndFilter(rWord);
|
||||
m_pQuickHlpData->Start( rSh, rWord.getLength() );
|
||||
m_pQuickHlpData->Start(rSh, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,8 +189,9 @@ class SW_DLLPUBLIC SwEditWin final : public vcl::Window,
|
||||
virtual OUString GetSurroundingText() const override;
|
||||
virtual Selection GetSurroundingTextSelection() const override;
|
||||
|
||||
void ShowAutoTextCorrectQuickHelp( const OUString& rWord, SvxAutoCorrCfg const * pACfg,
|
||||
SvxAutoCorrect* pACorr, bool bFromIME = false );
|
||||
void ShowAutoCorrectQuickHelp(const OUString& rWord, SvxAutoCorrect& rACorr,
|
||||
bool bFromIME = false);
|
||||
bool ShowAutoText(const std::vector<OUString>& rChunkCandidates);
|
||||
|
||||
/// Returns true if in header/footer area, or in the header/footer control.
|
||||
bool IsInHeaderFooter( const Point &rDocPt, FrameControlType &rControl ) const;
|
||||
|
@ -58,7 +58,8 @@ public:
|
||||
SwGlossaryList();
|
||||
virtual ~SwGlossaryList() override;
|
||||
|
||||
void HasLongName(const OUString& rBegin, std::vector<OUString> *pLongNames);
|
||||
void HasLongName(const std::vector<OUString>& rBeginCandidates,
|
||||
std::vector<std::pair<OUString, sal_uInt16>>& rLongNames);
|
||||
bool GetShortName(const OUString& rLongName,
|
||||
OUString& rShortName, OUString& rGroupName );
|
||||
|
||||
|
@ -364,32 +364,69 @@ void SwGlossaryList::FillGroup(AutoTextGroup* pGroup, SwGlossaries* pGlossaries)
|
||||
// Give back all (not exceeding FIND_MAX_GLOS) found modules
|
||||
// with matching beginning.
|
||||
|
||||
void SwGlossaryList::HasLongName(const OUString& rBegin, std::vector<OUString> *pLongNames)
|
||||
void SwGlossaryList::HasLongName(const std::vector<OUString>& rBeginCandidates,
|
||||
std::vector<std::pair<OUString, sal_uInt16>>& rLongNames)
|
||||
{
|
||||
if(!bFilled)
|
||||
Update();
|
||||
sal_uInt16 nFound = 0;
|
||||
const size_t nCount = aGroupArr.size();
|
||||
sal_Int32 nBeginLen = rBegin.getLength();
|
||||
const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
|
||||
// We store results for all candidate words in separate lists, so that later
|
||||
// we can sort them according to the candidate position
|
||||
std::vector<std::vector<OUString>> aResults(rBeginCandidates.size());
|
||||
|
||||
for(size_t i = 0; i < nCount; ++i)
|
||||
// We can't break after FIND_MAX_GLOS items found, since first items may have ended up in
|
||||
// lower-priority lists, and those from higher-priority lists are yet to come. So process all.
|
||||
for (const auto& pGroup : aGroupArr)
|
||||
{
|
||||
AutoTextGroup* pGroup = aGroupArr[i].get();
|
||||
sal_Int32 nIdx{ 0 };
|
||||
for(sal_uInt16 j = 0; j < pGroup->nCount; j++)
|
||||
{
|
||||
OUString sBlock = pGroup->sLongNames.getToken(0, STRING_DELIM, nIdx);
|
||||
if( nBeginLen + 1 < sBlock.getLength() &&
|
||||
rSCmp.isEqual( sBlock.copy(0, nBeginLen), rBegin ))
|
||||
for (size_t i = 0; i < rBeginCandidates.size(); ++i)
|
||||
{
|
||||
pLongNames->push_back( sBlock );
|
||||
nFound++;
|
||||
if(FIND_MAX_GLOS == nFound)
|
||||
break;
|
||||
const OUString& s = rBeginCandidates[i];
|
||||
if (s.getLength() + 1 < sBlock.getLength()
|
||||
&& rSCmp.isEqual(sBlock.copy(0, s.getLength()), s))
|
||||
{
|
||||
aResults[i].push_back(sBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<OUString, sal_uInt16>> aAllResults;
|
||||
// Sort and concatenate all result lists. See QuickHelpData::SortAndFilter
|
||||
for (size_t i = 0; i < rBeginCandidates.size(); ++i)
|
||||
{
|
||||
std::sort(aResults[i].begin(), aResults[i].end(),
|
||||
[origWord = rBeginCandidates[i]](const OUString& s1, const OUString& s2) {
|
||||
int nRet = s1.compareToIgnoreAsciiCase(s2);
|
||||
if (nRet == 0)
|
||||
{
|
||||
// fdo#61251 sort stuff that starts with the exact rOrigWord before
|
||||
// another ignore-case candidate
|
||||
int n1StartsWithOrig = s1.startsWith(origWord) ? 0 : 1;
|
||||
int n2StartsWithOrig = s2.startsWith(origWord) ? 0 : 1;
|
||||
return n1StartsWithOrig < n2StartsWithOrig;
|
||||
}
|
||||
return nRet < 0;
|
||||
});
|
||||
// All suggestions must be accompanied with length of the text they would replace
|
||||
std::transform(aResults[i].begin(), aResults[i].end(), std::back_inserter(aAllResults),
|
||||
[nLen = sal_uInt16(rBeginCandidates[i].getLength())](const OUString& s) {
|
||||
return std::make_pair(s, nLen);
|
||||
});
|
||||
}
|
||||
|
||||
const auto& it = std::unique(
|
||||
aAllResults.begin(), aAllResults.end(),
|
||||
[](const std::pair<OUString, sal_uInt16>& s1, const std::pair<OUString, sal_uInt16>& s2) {
|
||||
return s1.first.equalsIgnoreAsciiCase(s2.first);
|
||||
});
|
||||
if (const auto nCount = std::min<size_t>(std::distance(aAllResults.begin(), it), FIND_MAX_GLOS))
|
||||
{
|
||||
rLongNames.insert(rLongNames.end(), aAllResults.begin(), aAllResults.begin() + nCount);
|
||||
}
|
||||
}
|
||||
|
||||
void SwGlossaryList::ClearGroups()
|
||||
|
Loading…
x
Reference in New Issue
Block a user