/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/// because the Find may be called on the View or the Model, we need an index /// afflicted by multiple personality disorder struct AmbiguousIndex
{ private:
sal_Int32 m_value;
if ( bNewHint )
{ if (pNextHint && pNextHint->HasDummyChar() && (nStart <= nStt))
{ switch (pNextHint->Which())
{ case RES_TXTATR_FLYCNT: case RES_TXTATR_FIELD: case RES_TXTATR_REFMARK: case RES_TXTATR_TOXMARK: case RES_TXTATR_META: case RES_TXTATR_METAFIELD:
{ // (1998) they are desired as separators and // belong not any longer to a word. // they should also be ignored at a // beginning/end of a sentence if blank. Those are // simply removed if first. If at the end, we keep the // replacement and remove afterwards all at a string's // end (might be normal 0x7f). constbool bEmpty = pNextHint->Which() != RES_TXTATR_FIELD
|| (static_txtattr_cast<SwTextField const*>(pNextHint)->GetFormatField().GetField()->ExpandField(true, pLayout).isEmpty()); if ( bEmpty && nStart == nCurrent )
{
rArr.push_back( nCurrent ); if (rEnd.GetAnyIndex() > nCurrent.GetAnyIndex())
{
--rEnd.GetAnyIndex();
}
buf.remove(nCurrent.GetAnyIndex(), 1);
} else
{ if ( bEmpty )
aReplaced.push_back( nCurrent );
buf[nCurrent.GetAnyIndex()] = '\x7f';
}
} break; case RES_TXTATR_ANNOTATION:
{ if( bRemoveCommentAnchors )
{
rArr.push_back( nCurrent ); if (rEnd.GetAnyIndex() > nCurrent.GetAnyIndex())
{
--rEnd.GetAnyIndex();
}
buf.remove( nCurrent.GetAnyIndex(), 1 );
}
} break; default:
OSL_FAIL( "unknown case in lcl_CleanStr" ); break;
}
}
pNextHint = iter.NextAttr(pNextHintNode);
}
if ( bNewSoftHyphen )
{
rArr.push_back( nCurrent );
// If the soft hyphen to be removed is past the end of the range we're searching in, // don't adjust the end. if (rEnd.GetAnyIndex() > nCurrent.GetAnyIndex())
{
--rEnd.GetAnyIndex();
}
buf.remove(nCurrent.GetAnyIndex(), 1);
++nSoftHyphen.GetAnyIndex();
}
} while ( true );
for (auto i = aReplaced.size(); i; )
{ const AmbiguousIndex nTmp = aReplaced[ --i ]; if (nTmp.GetAnyIndex() == buf.getLength() - 1)
{
buf.truncate(nTmp.GetAnyIndex());
rArr.push_back( nTmp );
--rEnd.GetAnyIndex();
}
}
// If bFound is true then the string was found and is between nStart and nEnd bool bFound = false; // start position in text or initial position bool bFirst = true;
SwContentNode * pNode;
if (!xSearchItem)
{
xSearchItem.reset(new SvxSearchItem(SID_SEARCH_ITEM)); // this is a very expensive operation (calling configmgr etc.)
xSearchItem->SetSearchOptions(rSearchOpt);
xSearchItem->SetBackward(!bSrchForward);
}
// LanguageType eLastLang = 0; while (nullptr != (pNode = ::GetNode(*oPam, bFirst, fnMove, bInReadOnly, pLayout)))
{ if (!pNode->IsTextNode()) continue;
/* #i80135# */ // if there are SwPostItFields inside our current node text, we // split the text into separate pieces and search for text inside // the pieces as well as inside the fields
MaybeMergedIter iter(pLayout ? pFrame : nullptr, pLayout ? nullptr : &rTextNode);
// count PostItFields by looping over all fields
std::vector<std::pair<SwTextAttr const*, AmbiguousIndex>> postits; if (bSearchInNotes)
{ if (!bSrchForward)
{
std::swap(nStart, nEnd);
}
SwTextNode const* pTemp(nullptr); while (SwTextAttr const* const pTextAttr = iter.NextAttr(pTemp))
{ if (pTextAttr->Which() == RES_TXTATR_ANNOTATION)
{
AmbiguousIndex aPos;
aPos.SetModelIndex(pTextAttr->GetStart()); if (pLayout)
{
aPos.SetFrameIndex(pFrame->MapModelToView(pTemp, aPos.GetModelIndex()));
} if ((nStart <= aPos) && (aPos <= nEnd))
{
postits.emplace_back(pTextAttr, aPos);
}
}
}
// If there is an active text edit, then search there. bool bEndedTextEdit = false;
SdrView* pSdrView = pWrtShell ? pWrtShell->GetDrawView() : nullptr; if (pSdrView)
{ // If the edited object is not anchored to this node, then ignore it.
SdrObject* pObject = pSdrView->GetTextEditObject(); if (pObject)
{ if (SwFrameFormat* pFrameFormat = FindFrameFormat(pObject))
{ const SwNode* pAnchorNode = pFrameFormat->GetAnchor().GetAnchorNode(); if (!pAnchorNode
|| (pLayout ? !FrameContainsNode(*pFrame, pAnchorNode->GetIndex())
: pAnchorNode->GetIndex() != pNode->GetIndex()))
pObject = nullptr;
}
}
if (pObject)
{
sal_uInt16 nResult
= pSdrView->GetTextEditOutlinerView()->StartSearchAndReplace(*xSearchItem); if (!nResult)
{ // If not found, end the text edit.
pSdrView->SdrEndTextEdit(); const Point aPoint(pSdrView->GetAllMarkedRect().TopLeft());
pSdrView->UnmarkAll();
pWrtShell->CallSetCursor(&aPoint, true);
pWrtShell->Edit();
bEndedTextEdit = true;
} else
{
bFound = true; break;
}
}
}
if (comphelper::LibreOfficeKit::isActive())
{ // Writer and editeng selections are not supported in parallel.
SvxSearchItem* pSearchItem = SwView::GetSearchItem(); // If we just finished search in shape text, don't attempt to do that again. if (!bEndedTextEdit
&& !(pSearchItem && pSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL))
{ // If there are any shapes anchored to this node, search there.
SwPaM aPaM(pNode->GetDoc().GetNodes().GetEndOfContent()); if (pLayout)
{
*aPaM.GetPoint() = pFrame->MapViewToModelPos(nStart.GetFrameIndex());
} else
{
aPaM.GetPoint()->Assign(rTextNode, nStart.GetModelIndex());
}
aPaM.SetMark(); if (pLayout)
{
aPaM.GetMark()->Assign(
(pFrame->GetMergedPara() ? *pFrame->GetMergedPara()->pLastNode : rTextNode)
.GetIndex()
+ 1);
} else
{
aPaM.GetMark()->Assign(rTextNode.GetIndex() + 1);
} if (pNode->GetDoc().getIDocumentDrawModelAccess().Search(aPaM, *xSearchItem)
&& pSdrView)
{ if (SdrObject* pObject = pSdrView->GetTextEditObject())
{ if (SwFrameFormat* pFrameFormat = FindFrameFormat(pObject))
{ const SwNode* pAnchorNode = pFrameFormat->GetAnchor().GetAnchorNode(); if (pAnchorNode)
{ // Set search position to the shape's anchor point.
rSearchPam.GetPoint()->Assign(*pAnchorNode);
rSearchPam.SetMark();
bFound = true; break;
}
}
}
}
}
}
// do we need to finish a note? if (pPostItMgr && pPostItMgr->HasActiveSidebarWin())
{ if (bSearchInNotes)
{ if (!postits.empty())
{ if (bSrchForward)
{
postits.erase(postits.begin());
} else
{
postits.pop_back(); // hope that's the right one?
}
} //search inside, finish and put focus back into the doc if (pPostItMgr->FinishSearchReplace(rSearchOpt, bSrchForward))
{
bFound = true; break;
}
} else
{
pPostItMgr->SetActiveSidebarWin(nullptr);
}
}
if (!postits.empty())
{ // now we have to split
AmbiguousIndex nStartInside;
AmbiguousIndex nEndInside;
sal_Int32 aLoop = bSrchForward ? 0 : postits.size();
while ((0 <= aLoop) && (o3tl::make_unsigned(aLoop) <= postits.size()))
{ if (bSrchForward)
{ if (aLoop == 0)
{
nStartInside = nStart;
} elseif (pLayout)
{
nStartInside.SetFrameIndex(postits[aLoop - 1].second.GetFrameIndex()
+ TextFrameIndex(1));
} else
{
nStartInside.SetModelIndex(postits[aLoop - 1].second.GetModelIndex() + 1);
}
nEndInside = static_cast<size_t>(aLoop) == postits.size()
? nEnd
: postits[aLoop].second;
nTextLen = nEndInside - nStartInside;
} else
{
nStartInside = static_cast<size_t>(aLoop) == postits.size()
? nStart
: postits[aLoop].second; if (aLoop == 0)
{
nEndInside = nEnd;
} elseif (pLayout)
{
nEndInside.SetFrameIndex(postits[aLoop - 1].second.GetFrameIndex()
+ TextFrameIndex(1));
} else
{
nEndInside.SetModelIndex(postits[aLoop - 1].second.GetModelIndex() + 1);
}
nTextLen = nStartInside - nEndInside;
} // search inside the text between a note
bFound = DoSearch(rSearchPam, rSearchOpt, rSText, fnMove, bSrchForward, bRegSearch,
bChkEmptyPara, bChkParaEnd, nStartInside, nEndInside, nTextLen,
pNode->GetTextNode(), pFrame, pLayout, *oPam); if (bFound) break; else
{ // we should now be right in front of a note, search inside if (bSrchForward ? (static_cast<size_t>(aLoop) != postits.size())
: (aLoop != 0))
{ const SwTextAttr* const pTextAttr
= bSrchForward ? postits[aLoop].first : postits[aLoop - 1].first; if (pPostItMgr
&& pPostItMgr->SearchReplace(
static_txtattr_cast<SwTextField const*>(pTextAttr)
->GetFormatField(),
rSearchOpt, bSrchForward))
{
bFound = true; break;
}
}
}
aLoop = bSrchForward ? aLoop + 1 : aLoop - 1;
}
} else
{ // if there is no SwPostItField inside or searching inside notes // is disabled, we search the whole length just like before
bFound = DoSearch(rSearchPam, rSearchOpt, rSText, fnMove, bSrchForward, bRegSearch,
bChkEmptyPara, bChkParaEnd, nStart, nEnd, nTextLen,
pNode->GetTextNode(), pFrame, pLayout, *oPam);
} if (bFound) break;
} return bFound;
}
} // namespace sw
bool DoSearch(SwPaM & rSearchPam, const i18nutil::SearchOptions2& rSearchOpt, utl::TextSearch& rSText,
SwMoveFnCollection const & fnMove, bool bSrchForward, bool bRegSearch, bool bChkEmptyPara, bool bChkParaEnd,
AmbiguousIndex & nStart, AmbiguousIndex & nEnd, AmbiguousIndex const nTextLen,
SwTextNode const*const pNode, SwTextFrame const*const pFrame,
SwRootFrame const*const pLayout, SwPaM& rPam)
{ if (bRegSearch && rSearchOpt.searchString.endsWith("$"))
{ bool bAlwaysSearchingForEndOfPara = true;
sal_Int32 nIndex = 0; while ((nIndex = rSearchOpt.searchString.indexOf("|", nIndex)) != -1)
{ if (!nIndex || rSearchOpt.searchString[nIndex - 1] != '$')
{
bAlwaysSearchingForEndOfPara = false; break;
}
++nIndex;
} // when searching for something at the end of the paragraph, the para end must be in range const AmbiguousIndex& rParaEnd = bSrchForward ? nEnd : nStart; if (bAlwaysSearchingForEndOfPara && nTextLen.GetAnyIndex() != rParaEnd.GetAnyIndex()) returnfalse;
}
bool bFound = false;
OUString sCleanStr;
std::vector<AmbiguousIndex> aFltArr;
LanguageType eLastLang = LANGUAGE_SYSTEM; // if the search string contains a soft hyphen, // we don't strip them from the text: bool bRemoveSoftHyphens = true; // if the search string contains a comment, we don't strip them from the text constbool bRemoveCommentAnchors = rSearchOpt.searchString.indexOf( CH_TXTATR_INWORD ) == -1;
const AmbiguousIndex nStringEnd = nEnd; bool bZeroMatch = false; // zero-length match, i.e. only $ anchor as regex while ( ((bSrchForward && nStart < nStringEnd) ||
(!bSrchForward && nStringEnd < nStart)) && !bZeroMatch )
{ // SearchAlgorithms_APPROXIMATE works on a per word base so we have to // provide the text searcher with the correct locale, because it uses // the break-iterator if ( pScriptIter )
{
nEnd.GetAnyIndex() = pScriptIter->GetScriptChgPos();
nCurrScript = pScriptIter->GetCurrScript(); if ( nSearchScript == nCurrScript )
{ const LanguageType eCurrLang = pLayout
? pFrame->GetLangOfChar(bSrchForward
? nStart.GetFrameIndex()
: nEnd.GetFrameIndex(),
0, true)
: pNode->GetLang(bSrchForward
? nStart.GetModelIndex()
: nEnd.GetModelIndex());
// if backward search, switch point and mark if( !bSrchForward )
rSearchPam.Exchange();
bFound = true; break;
} else
{
nEnd = nProxyEnd;
}
nStart = nEnd;
}
pScriptIter.reset();
if ( bFound ) returntrue;
if (!bChkEmptyPara && !bChkParaEnd) returnfalse;
if (bChkEmptyPara && bSrchForward && nTextLen.GetAnyIndex()) returnfalse; // the length is not zero - there is content here
// move to the end (or start) of the paragraph
*rSearchPam.GetPoint() = *rPam.GetPoint(); if (pLayout)
{
*rSearchPam.GetPoint() = pFrame->MapViewToModelPos(
bSrchForward ? nTextLen.GetFrameIndex() : TextFrameIndex(0));
} else
{
rSearchPam.GetPoint()->SetContent(bSrchForward ? nTextLen.GetModelIndex() : 0);
}
rSearchPam.SetMark();
if (!rSearchPam.Move(fnMove, GoInContent)) returnfalse; // at start or end of the document
// selection must not be outside of the search area if (!rPam.ContainsPosition(*rSearchPam.GetPoint())) returnfalse;
if (SwNodeOffset(1) == abs(rSearchPam.GetPoint()->GetNodeIndex() -
rSearchPam.GetMark()->GetNodeIndex()))
{ if (bChkEmptyPara && !bSrchForward && rSearchPam.GetPoint()->GetContentIndex()) returnfalse; // the length is not zero - there is content here
returntrue;
}
returnfalse;
}
namespace {
/// parameters for search and replace in text struct SwFindParaText : public SwFindParas
{ const i18nutil::SearchOptions2& m_rSearchOpt;
SwCursor& m_rCursor;
SwRootFrame const* m_pLayout;
utl::TextSearch m_aSText; bool m_bReplace; bool m_bSearchInNotes;
if( bFnd && m_bReplace ) // replace string
{ // use replace method in SwDoc constbool bRegExp(SearchAlgorithms2::REGEXP == m_rSearchOpt.AlgorithmType2); const sal_Int32 nSttCnt = rCursor.Start()->GetContentIndex(); // add to shell-cursor-ring so that the regions will be moved eventually
SwPaM* pPrev(nullptr); if( bRegExp )
{
pPrev = const_cast<SwPaM&>(rRegion).GetPrev(); const_cast<SwPaM&>(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() );
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.