/* -*- 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 .
*/
// CreateNewSavePos is virtual so that derived classes of cursor can implement // own SaveObjects if needed and validate them in the virtual check routines. void SwCursor::SaveState()
{
m_vSavePos.emplace_back( *this );
}
/// determine if point is outside of the node-array's content area bool SwCursor::IsNoContent() const
{ return GetPoint()->GetNodeIndex() <
GetDoc().GetNodes().GetEndOfExtras().GetIndex();
}
// is there a protected section in the section? if( HasMark() && bSkipOverProtectSections)
{
SwNodeOffset nSttIdx = GetMark()->GetNodeIndex(),
nEndIdx = GetPoint()->GetNodeIndex(); if( nEndIdx <= nSttIdx )
std::swap( nSttIdx, nEndIdx );
const SwSectionFormats& rFormats = rDoc.GetSections(); for( SwSectionFormats::size_type n = 0; n < rFormats.size(); ++n )
{ const SwSectionFormat* pFormat = rFormats[n]; const SvxProtectItem& rProtect = pFormat->GetProtect(); if( rProtect.IsContentProtected() )
{ const SwFormatContent& rContent = pFormat->GetContent(false);
OSL_ENSURE( rContent.GetContentIdx(), "No SectionNode?" );
SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex(); if( nSttIdx <= nIdx && nEndIdx >= nIdx )
{ // if it is no linked section then we cannot select it const SwSection& rSect = *pFormat->GetSection(); if( SectionType::Content == rSect.GetType() )
{
RestoreSavePos(); returntrue;
}
}
}
}
}
}
const SwNode* pNd = &GetPoint()->GetNode(); if( pNd->IsContentNode() && !dynamic_cast<SwUnoCursor*>(this) )
{ const SwContentFrame* pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ); if ( (SwCursorSelOverFlags::ChangePos & eFlags) //allowed to change position if it's a bad one
&& pFrame && pFrame->isFrameAreaDefinitionValid()
&& !pFrame->getFrameArea().Height() //a bad zero height position
&& !InputFieldAtPos(GetPoint()) ) //unless it's a (vertical) input field
{ // skip to the next/prev valid paragraph with a layout
SwPosition& rPtPos = *GetPoint(); bool bGoNxt = m_vSavePos.back().nNode < rPtPos.GetNodeIndex(); for (;;)
{
pFrame = bGoNxt ? pFrame->FindNextCnt(true) : pFrame->FindPrevCnt(); if (!pFrame || 0 != pFrame->getFrameArea().Height() ) break;
}
// #i72394# skip to prev/next valid paragraph with a layout in case // the first search did not succeed: if( !pFrame )
{
bGoNxt = !bGoNxt;
pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ); while ( pFrame && 0 == pFrame->getFrameArea().Height() )
{
pFrame = bGoNxt ? pFrame->FindNextCnt(true) : pFrame->FindPrevCnt();
}
}
// set this ContentNode as new position
rPtPos.Assign( *pCNd ); // assign corresponding ContentIndex const sal_Int32 nTmpPos = bGoNxt ? 0 : pCNd->Len();
GetPoint()->SetContent( nTmpPos );
}
if (rPtPos.GetNodeIndex() == m_vSavePos.back().nNode
&& GetPoint()->GetContentIndex() == m_vSavePos.back().nContent)
{ // new position equals saved one // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
pFrame = nullptr;
}
if ( IsInProtectTable( true ) )
{ // new position in protected table // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
pFrame = nullptr;
}
}
}
if( !pFrame )
{
assert(!m_vSavePos.empty());
SwContentNode const*const pSaveNode(rNds[m_vSavePos.back().nNode]->GetContentNode()); // if the old position already didn't have a frame, allow moving // anyway, hope the caller can handle that if (pSaveNode && pSaveNode->getLayoutFrame(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()))
{
DeleteMark();
RestoreSavePos(); returntrue; // we need a frame
}
}
}
// is the cursor allowed to be in a protected node? if( !( SwCursorSelOverFlags::ChangePos & eFlags ) && !IsAtValidPos() )
{
DeleteMark();
RestoreSavePos(); returntrue;
}
if( !HasMark() ) returnfalse;
// check for invalid sections if( !::CheckNodesRange( GetMark()->GetNode(), GetPoint()->GetNode(), true ))
{
DeleteMark();
RestoreSavePos(); returntrue; // we need a frame
}
pNd = &GetMark()->GetNode(); if( pNd->IsContentNode()
&& !static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() )
&& !dynamic_cast<SwUnoCursor*>(this) )
{
DeleteMark();
RestoreSavePos(); returntrue; // we need a frame
}
// ensure that selection is only inside an InputField or contains the InputField completely
{ const SwTextAttr* pInputFieldTextAttrAtPoint = InputFieldAtPos(GetPoint()); const SwTextAttr* pInputFieldTextAttrAtMark = InputFieldAtPos(GetMark());
const SwTableNode* pPtNd = GetPoint()->GetNode().FindTableNode(); const SwTableNode* pMrkNd = GetMark()->GetNode().FindTableNode(); // both in no or in same table node if( ( !pMrkNd && !pPtNd ) || pPtNd == pMrkNd ) returnfalse;
// in different tables or only mark in table if( pMrkNd )
{ // not allowed, so go back to old position
RestoreSavePos(); // Cursor stays at old position returntrue;
}
// Note: this cannot happen in TableMode // Only Point in Table then go behind/in front of table if (SwCursorSelOverFlags::ChangePos & eFlags)
{ bool bSelTop = GetPoint()->GetNodeIndex() <
((SwCursorSelOverFlags::Toggle & eFlags)
? m_vSavePos.back().nNode : GetMark()->GetNodeIndex());
do { // loop for table after table
SwNodeOffset nSEIdx = pPtNd->EndOfSectionIndex();
SwNodeOffset nSttEndTable = nSEIdx + 1;
// No table, no protected cell: const SwTableNode* pTableNode = pCNd->FindTableNode(); if ( !pTableNode ) returnfalse;
// Current position == last save position? if (m_vSavePos.back().nNode == GetPoint()->GetNodeIndex()) returnfalse;
// Check for covered cell: bool bInCoveredCell = false; const SwStartNode* pTmpSttNode = pCNd->FindTableBoxStartNode();
OSL_ENSURE( pTmpSttNode, "In table, therefore I expect to get a SwTableBoxStartNode" ); const SwTableBox* pBox = pTmpSttNode ? pTableNode->GetTable().GetTableBox( pTmpSttNode->GetIndex() ) : nullptr; //Robust #151355 if ( pBox && pBox->getRowSpan() < 1 ) // Robust #151270
bInCoveredCell = true;
// Positions of covered cells are not acceptable: if ( !bInCoveredCell )
{ // Position not protected? if ( !pCNd->IsProtect() ) returnfalse;
// Cursor in protected cells allowed? if ( IsReadOnlyAvailable() ) returnfalse;
}
// If we reach this point, we are in a protected or covered table cell!
if( !bMove )
{ if( bChgCursor ) // restore the last save position
RestoreSavePos();
returntrue; // Cursor stays at old position
}
// We are in a protected table cell. Traverse top to bottom? if (m_vSavePos.back().nNode < GetPoint()->GetNodeIndex())
{ // search next valid box // if there is another StartNode after the EndNode of a cell then // there is another cell
SwNodeIndex aCellStt( *GetPointNode().FindTableBoxStartNode()->EndOfSectionNode(), 1 ); bool bProt = true;
GoNextCell: for (;;) { if( !aCellStt.GetNode().IsStartNode() ) break;
++aCellStt;
pCNd = aCellStt.GetNode().GetContentNode(); if( !pCNd )
pCNd = SwNodes::GoNext(&aCellStt);
bProt = pCNd->IsProtect(); if( !bProt ) break;
aCellStt.Assign( *pCNd->FindTableBoxStartNode()->EndOfSectionNode(), 1 );
}
SetNextCursor: if( !bProt ) // found free cell
{
GetPoint()->Assign( aCellStt );
SwContentNode* pTmpCNd = GetPointContentNode(); if( pTmpCNd )
{
GetPoint()->SetContent( 0 ); returnfalse;
} return IsSelOvr( SwCursorSelOverFlags::Toggle |
SwCursorSelOverFlags::ChangePos );
} // end of table, so go to next node
++aCellStt;
SwNode* pNd = &aCellStt.GetNode(); if( pNd->IsEndNode() || HasMark())
{ // if only table in FlyFrame or SSelection then stay on old position if( bChgCursor )
RestoreSavePos(); returntrue;
} elseif( pNd->IsTableNode() )
{
++aCellStt; goto GoNextCell;
}
bProt = false; // index is now on a content node goto SetNextCursor;
}
// search for the previous valid box
{ // if there is another EndNode in front of the StartNode than there // exists a previous cell
SwNodeIndex aCellStt( *GetPointNode().FindTableBoxStartNode(), -1 );
SwNode* pNd; bool bProt = true;
GoPrevCell: for (;;) {
pNd = &aCellStt.GetNode(); if( !pNd->IsEndNode() ) break;
aCellStt.Assign( *pNd->StartOfSectionNode(), +1 );
pCNd = aCellStt.GetNode().GetContentNode(); if( !pCNd )
pCNd = SwNodes::GoNext(&aCellStt);
bProt = pCNd->IsProtect(); if( !bProt ) break;
aCellStt.Assign( *pNd->FindTableBoxStartNode(), -1 );
}
SetPrevCursor: if( !bProt ) // found free cell
{
GetPoint()->Assign( aCellStt );
SwContentNode* pTmpCNd = GetPointContentNode(); if( pTmpCNd )
{
GetPoint()->SetContent( 0 ); returnfalse;
} return IsSelOvr( SwCursorSelOverFlags::Toggle |
SwCursorSelOverFlags::ChangePos );
} // at the beginning of a table, so go to next node
--aCellStt;
pNd = &aCellStt.GetNode(); if( pNd->IsStartNode() || HasMark() )
{ // if only table in FlyFrame or SSelection then stay on old position if( bChgCursor )
RestoreSavePos(); returntrue;
} elseif( pNd->StartOfSectionNode()->IsTableNode() )
{
--aCellStt; goto GoPrevCell;
}
bProt = false; // index is now on a content node goto SetPrevCursor;
}
}
/// Return <true> if cursor can be set to this position bool SwCursor::IsAtValidPos( bool bPoint ) const
{ const SwDoc& rDoc = GetDoc(); const SwPosition* pPos = bPoint ? GetPoint() : GetMark(); const SwNode* pNd = &pPos->GetNode();
bool bEnd = false; do {
aRegion.SetMark(); // independent from search direction: SPoint is always bigger than mark // if the search area is valid
SwPosition *pSttPos = aRegion.GetMark(),
*pEndPos = aRegion.GetPoint();
*pSttPos = *pTmpCursor->Start();
*pEndPos = *pTmpCursor->End(); if( bSrchBkwrd )
aRegion.Exchange();
// as long as found and not at same position while( *pSttPos <= *pEndPos )
{
nFndRet = rParas.DoFind(*pCurrentCursor, fnMove, aRegion, bInReadOnly, xSearchItem); if( 0 == nFndRet ||
( pFndRing &&
*pFndRing->GetPoint() == *pCurrentCursor->GetPoint() &&
*pFndRing->GetMark() == *pCurrentCursor->GetMark() )) break; if( !( FIND_NO_RING & nFndRet ))
{ // #i24084# - create ring similar to the one in CreateCursor
SwCursor* pNew = pCurrentCursor->Create( pFndRing ); if( !pFndRing )
pFndRing = pNew;
if( bSrchBkwrd ) // move pEndPos in front of the found area
*pEndPos = *pCurrentCursor->Start(); else // move pSttPos behind the found area
*pSttPos = *pCurrentCursor->End();
if( *pSttPos == *pEndPos ) // in area but at the end => done break;
// this method "searches" for all use cases because in SwFindParas is always the // correct parameters and respective search method
sal_Int32 SwCursor::FindAll( SwFindParas& rParas,
SwDocPositions nStart, SwDocPositions nEnd,
FindRanges eFndRngs, bool& bCancel )
{
bCancel = false;
SwCursorSaveState aSaveState( *this );
// create region without adding it to the ring
SwPaM aRegion( *GetPoint() );
SwMoveFnCollection const & fnMove = MakeFindRange( nStart, nEnd, &aRegion );
// search in sections? if( FindRanges::InSel & eFndRngs )
{ // if string was not found in region then get all sections (cursors // stays unchanged)
nFound = lcl_FindSelection( rParas, this, fnMove,
pFndRing, aRegion, eFndRngs,
bInReadOnly, bCancel ); if( 0 == nFound ) return nFound;
// found string at least once; it's all in new Cursor ring thus delete old one while( GetNext() != this ) delete GetNext();
*GetPoint() = *pFndRing->GetPoint();
SetMark();
*GetMark() = *pFndRing->GetMark();
pFndRing->GetRingContainer().merge( GetRingContainer() ); delete pFndRing;
} elseif( FindRanges::InOther & eFndRngs )
{ // put cursor as copy of current into ring // chaining points always to first created, so forward
SwCursor* pSav = Create( this ); // save the current cursor
// if already outside of body text search from this position or start at // 1. base section if( bMvBkwrd
? lcl_MakeSelBkwrd( rNds.GetEndOfExtras(),
*rNds.GetEndOfPostIts().StartOfSectionNode(),
*this, rNds.GetEndOfExtras().GetIndex() >=
GetPoint()->GetNodeIndex() )
: lcl_MakeSelFwrd( *rNds.GetEndOfPostIts().StartOfSectionNode(),
rNds.GetEndOfExtras(), *this,
rNds.GetEndOfExtras().GetIndex() >=
GetPoint()->GetNodeIndex() ))
{
nFound = lcl_FindSelection( rParas, this, fnMove, pFndRing,
aRegion, eFndRngs, bInReadOnly, bCancel );
}
if( !nFound )
{ // put back the old one
*GetPoint() = *pSav->GetPoint(); if( pSav->HasMark() )
{
SetMark();
*GetMark() = *pSav->GetMark();
} else
DeleteMark(); return 0;
}
if( !( FindRanges::InSelAll & eFndRngs ))
{ // there should only be a single one, thus add it // independent from search direction: SPoint is always bigger than // mark if the search area is valid
*GetPoint() = *pFndRing->GetPoint();
SetMark();
*GetMark() = *pFndRing->GetMark();
} else
{ // found string at least once; it's all in new Cursor ring thus delete old one while( GetNext() != this ) delete GetNext();
*GetPoint() = *pFndRing->GetPoint();
SetMark();
*GetMark() = *pFndRing->GetMark();
pFndRing->GetRingContainer().merge( GetRingContainer() );
} delete pFndRing;
} elseif( FindRanges::InSelAll & eFndRngs )
{
SwCursor* pSav = Create( this ); // save the current cursor
if( !nFound )
{ // put back the old one
*GetPoint() = *pSav->GetPoint(); if( pSav->HasMark() )
{
SetMark();
*GetMark() = *pSav->GetMark();
} else
DeleteMark(); return 0;
} while( GetNext() != this ) delete GetNext();
*GetPoint() = *pFndRing->GetPoint();
SetMark();
*GetMark() = *pFndRing->GetMark();
pFndRing->GetRingContainer().merge( GetRingContainer() ); delete pFndRing;
} else
{ // if a GetMark is set then keep the GetMark of the found object // This allows spanning an area with this search.
SwPosition aMarkPos( *GetMark() ); constbool bMarkPos = HasMark() && (eFndRngs == FindRanges::InBody);
short SwCursor::MaxReplaceArived()
{ return RET_YES;
}
namespace {
struct HideWrapper
{ // either the frame's text or the node's text (possibly pre-filtered)
OUString const* m_pText; // this is actually a TextFrameIndex but all of the i18n code uses sal_Int32
sal_Int32 m_nPtIndex; // if mapping is needed, use this frame
SwTextFrame * m_pFrame; // input in the constructor, output (via mapping) in the destructor
SwTextNode *& m_rpTextNode;
sal_Int32 & m_rPtPos;
if (comphelper::LibreOfficeKit::isActive() && aBndry.startPos == aBndry.endPos && w.m_nPtIndex > 0)
{ // nPtPos is the end of the paragraph, select the last word then.
--w.m_nPtIndex;
w.AssignBack(pTextNd, nPtPos);
switch ( eMoveType )
{ case START_SENT: /* when modifying: see also ExpandToSentenceBorders below! */
w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
*w.m_pText, w.m_nPtIndex,
g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); break; case END_SENT: /* when modifying: see also ExpandToSentenceBorders below! */
w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
*w.m_pText, w.m_nPtIndex,
g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); break; case NEXT_SENT:
{
w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
*w.m_pText, w.m_nPtIndex,
g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); if (w.m_nPtIndex >= 0 && w.m_nPtIndex < w.m_pText->getLength())
{ do
{
++w.m_nPtIndex;
} while (w.m_nPtIndex < w.m_pText->getLength()
&& (*w.m_pText)[w.m_nPtIndex] == ' ');
} break;
} case PREV_SENT:
w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
*w.m_pText, w.m_nPtIndex,
g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
if (w.m_nPtIndex == 0) returnfalse; // the previous sentence is not in this paragraph if (w.m_nPtIndex > 0)
{
w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
*w.m_pText, w.m_nPtIndex - 1,
g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
} break;
}
}
// it is allowed to place the PaM just behind the last // character in the text thus <= ...Len if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0)
{
GetPoint()->Assign(*pTextNd, nPtPos); if( !IsSelOvr() )
bRet = true;
}
} return bRet;
}
// it is allowed to place the PaM just behind the last // character in the text thus <= ...Len if (nStartPos <= pStartNd->GetText().getLength() && nStartPos >= 0)
{
GetMark()->Assign(*pStartNd, nStartPos);
} if (nEndPos <= pEndNd->GetText().getLength() && nEndPos >= 0)
{
GetPoint()->Assign(*pEndNd, nEndPos);
}
}
if ( !Move( fnMove, fnGo ) )
{ const SwEditShell* pSh = GetDoc().GetEditShell(); const SwViewOption* pViewOptions = pSh ? pSh->GetViewOptions() : nullptr; if (pViewOptions && pViewOptions->IsShowOutlineContentVisibilityButton())
{ // Fixes crash that occurs in documents with outline content folded at the end of // the document. When the cursor is at the end of the visible document and // right arrow key is pressed Move fails after moving the cursor to the // end of the document model, which doesn't have a node frame and causes // weird numbers to be displayed in the statusbar page number count. Left // arrow, when in this state, causes a crash without RestoredSavePos() added here.
RestoreSavePos();
} break;
}
if (pFrame)
{
SwTextFrame const* pNewFrame = GetTextFrame(GetPoint()->GetNode(), pLayout); // sw_redlinehide: fully redline-deleted nodes don't have frames... if (pFrame == pNewFrame || !pNewFrame)
{ if (!pNewFrame || beforeIndex == pFrame->MapModelToViewPos(*GetPoint()))
{ continue; // moving inside delete redline, doesn't count...
}
} else
{ // assume iteration is stable & returns the same frame
assert(!pFrame->IsAnFollow(pNewFrame) && !pNewFrame->IsAnFollow(pFrame));
pFrame = pNewFrame;
}
}
if (isFieldNames)
{
SwTextNode const*const pNode(GetPoint()->GetNode().GetTextNode());
assert(pNode);
SwTextAttr const*const pInputField(pNode->GetTextAttrAt(
GetPoint()->GetContentIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent)); if (pInputField)
{ continue; // skip over input fields
}
}
// If we were located inside a covered cell but our position has been // corrected, we check if the last move has moved the cursor to a // different table cell. In this case we set the cursor to the stored // covered position and redo the move: if (m_nRowSpanOffset)
{ const SwNode* pOldTabBoxSttNode = aOldNodeIdx.GetNode().FindTableBoxStartNode(); const SwTableNode* pOldTabSttNode = pOldTabBoxSttNode ? pOldTabBoxSttNode->FindTableNode() : nullptr;
--> --------------------
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.