/* -*- 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 .
*/
#include <tools/gen.hxx>
#include <hintids.hxx>
#include <editeng/protitem.hxx>
#include <cntfrm.hxx>
#include <pagefrm.hxx>
#include <doc.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <docary.hxx>
#include <pam.hxx>
#include <pamtyp.hxx>
#include <txtfrm.hxx>
#include <fmtcntnt.hxx>
#include <frmatr.hxx>
#include <swtable.hxx>
#include <flyfrm.hxx>
#include <fmteiro.hxx>
#include <section.hxx>
#include <sectfrm.hxx>
#include <ndtxt.hxx>
#include <swcrsr.hxx>
#include <IMark.hxx>
#include <DocumentSettingManager.hxx>
#include <hints.hxx>
#include <xmloff/odffields.hxx>
#include <editsh.hxx>
// for the dump "MSC-" compiler
inline sal_Int32 GetSttOrEnd( bool bCondition, const SwContentNode& rNd )
{
return bCondition ? 0 : rNd.Len();
}
SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, const SwIndex & rContent )
: nNode( rNodeIndex ), nContent( rContent )
{
}
SwPosition::SwPosition( const SwNodeIndex & rNodeIndex )
: nNode( rNodeIndex ), nContent( nNode.GetNode().GetContentNode() )
{
}
SwPosition::SwPosition( const SwNode& rNode )
: nNode( rNode ), nContent( nNode.GetNode().GetContentNode() )
{
}
SwPosition::SwPosition( SwContentNode & rNode, const sal_Int32 nOffset )
: nNode( rNode ), nContent( &rNode, nOffset )
{
}
bool SwPosition::operator<(const SwPosition &rPos) const
{
if( nNode < rPos.nNode )
return true;
if( nNode == rPos.nNode )
{
// note that positions with text node but no SwIndex registered are
// created for text frames anchored at para (see SwXFrame::getAnchor())
SwIndexReg const*const pThisReg(nContent.GetIdxReg());
SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
if (pThisReg && pOtherReg)
{
return (nContent < rPos.nContent);
}
else // by convention position with no index is smaller
{
return pOtherReg != nullptr;
}
}
return false;
}
bool SwPosition::operator>(const SwPosition &rPos) const
{
if(nNode > rPos.nNode )
return true;
if( nNode == rPos.nNode )
{
// note that positions with text node but no SwIndex registered are
// created for text frames anchored at para (see SwXFrame::getAnchor())
SwIndexReg const*const pThisReg(nContent.GetIdxReg());
SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
if (pThisReg && pOtherReg)
{
return (nContent > rPos.nContent);
}
else // by convention position with no index is smaller
{
return pThisReg != nullptr;
}
}
return false;
}
bool SwPosition::operator<=(const SwPosition &rPos) const
{
if(nNode < rPos.nNode )
return true;
if( nNode == rPos.nNode )
{
// note that positions with text node but no SwIndex registered are
// created for text frames anchored at para (see SwXFrame::getAnchor())
SwIndexReg const*const pThisReg(nContent.GetIdxReg());
SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
if (pThisReg && pOtherReg)
{
return (nContent <= rPos.nContent);
}
else // by convention position with no index is smaller
{
return pThisReg == nullptr;
}
}
return false;
}
bool SwPosition::operator>=(const SwPosition &rPos) const
{
if(nNode > rPos.nNode )
return true;
if( nNode == rPos.nNode )
{
// note that positions with text node but no SwIndex registered are
// created for text frames anchored at para (see SwXFrame::getAnchor())
SwIndexReg const*const pThisReg(nContent.GetIdxReg());
SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
if (pThisReg && pOtherReg)
{
return (nContent >= rPos.nContent);
}
else // by convention position with no index is smaller
{
return pOtherReg == nullptr;
}
}
return false;
}
bool SwPosition::operator==(const SwPosition &rPos) const
{
return (nNode == rPos.nNode)
&& (nContent == rPos.nContent);
}
bool SwPosition::operator!=(const SwPosition &rPos) const
{
return (nNode != rPos.nNode)
|| (nContent != rPos.nContent);
}
SwDoc * SwPosition::GetDoc() const
{
return nNode.GetNode().GetDoc();
}
void SwPosition::dumpAsXml(xmlTextWriterPtr pWriter) const
{
xmlTextWriterStartElement(pWriter, BAD_CAST("SwPosition"));
xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nNode"), BAD_CAST(OString::number(nNode.GetIndex()).getStr()));
xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nContent"), BAD_CAST(OString::number(nContent.GetIndex()).getStr()));
xmlTextWriterEndElement(pWriter);
}
std::ostream &operator <<(std::ostream& s, const SwPosition& position)
{
return s << "SwPosition (node " << position.nNode.GetIndex() << ", offset " << position.nContent.GetIndex() << ")";
}
enum CHKSECTION { Chk_Both, Chk_One, Chk_None };
static CHKSECTION lcl_TstIdx( sal_uLong nSttIdx, sal_uLong nEndIdx, const SwNode& rEndNd )
{
sal_uLong nStt = rEndNd.StartOfSectionIndex(), nEnd = rEndNd.GetIndex();
CHKSECTION eSec = nStt < nSttIdx && nEnd >= nSttIdx ? Chk_One : Chk_None;
if( nStt < nEndIdx && nEnd >= nEndIdx )
return( eSec == Chk_One ? Chk_Both : Chk_One );
return eSec;
}
static bool lcl_ChkOneRange( CHKSECTION eSec, bool bChkSections,
const SwNode& rBaseEnd, sal_uLong nStt, sal_uLong nEnd )
{
if( eSec != Chk_Both )
return false;
if( !bChkSections )
return true;
// search the surrounding section
const SwNodes& rNds = rBaseEnd.GetNodes();
const SwNode *pTmp, *pNd = rNds[ nStt ];
if( !pNd->IsStartNode() )
pNd = pNd->StartOfSectionNode();
if( pNd == rNds[ nEnd ]->StartOfSectionNode() )
return true; // same StartNode, same section
// already on a base node => error
if( !pNd->StartOfSectionIndex() )
return false;
while( ( pTmp = pNd->StartOfSectionNode())->EndOfSectionNode() !=
&rBaseEnd )
pNd = pTmp;
sal_uLong nSttIdx = pNd->GetIndex(), nEndIdx = pNd->EndOfSectionIndex();
return nSttIdx <= nStt && nStt <= nEndIdx &&
nSttIdx <= nEnd && nEnd <= nEndIdx;
}
bool CheckNodesRange( const SwNodeIndex& rStt,
const SwNodeIndex& rEnd, bool bChkSection )
{
const SwNodes& rNds = rStt.GetNodes();
sal_uLong nStt = rStt.GetIndex(), nEnd = rEnd.GetIndex();
CHKSECTION eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfContent() );
if( Chk_None != eSec )
return eSec == Chk_Both;
eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfAutotext() );
if( Chk_None != eSec )
return lcl_ChkOneRange( eSec, bChkSection,
rNds.GetEndOfAutotext(), nStt, nEnd );
eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfPostIts() );
if( Chk_None != eSec )
return lcl_ChkOneRange( eSec, bChkSection,
rNds.GetEndOfPostIts(), nStt, nEnd );
eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfInserts() );
if( Chk_None != eSec )
return lcl_ChkOneRange( eSec, bChkSection,
rNds.GetEndOfInserts(), nStt, nEnd );
eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfRedlines() );
if( Chk_None != eSec )
return lcl_ChkOneRange( eSec, bChkSection,
rNds.GetEndOfRedlines(), nStt, nEnd );
return false; // somewhere in between => error
}
bool GoNext(SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode )
{
if( pNd->IsContentNode() )
return static_cast<SwContentNode*>(pNd)->GoNext( pIdx, nMode );
return false;
}
bool GoPrevious( SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode )
{
if( pNd->IsContentNode() )
return static_cast<SwContentNode*>(pNd)->GoPrevious( pIdx, nMode );
return false;
}
SwContentNode* GoNextNds( SwNodeIndex* pIdx, bool bChk )
{
SwNodeIndex aIdx( *pIdx );
SwContentNode* pNd = aIdx.GetNodes().GoNext( &aIdx );
if( pNd )
{
if( bChk && 1 != aIdx.GetIndex() - pIdx->GetIndex() &&
!CheckNodesRange( *pIdx, aIdx, true ) )
pNd = nullptr;
else
*pIdx = aIdx;
}
return pNd;
}
SwContentNode* GoPreviousNds( SwNodeIndex * pIdx, bool bChk )
{
SwNodeIndex aIdx( *pIdx );
SwContentNode* pNd = SwNodes::GoPrevious( &aIdx );
if( pNd )
{
if( bChk && 1 != pIdx->GetIndex() - aIdx.GetIndex() &&
!CheckNodesRange( *pIdx, aIdx, true ) )
pNd = nullptr;
else
*pIdx = aIdx;
}
return pNd;
}
SwPaM::SwPaM( const SwPosition& rPos, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rPos )
, m_Bound2( rPos.nNode.GetNode().GetNodes() ) // default initialize
, m_pPoint( &m_Bound1 )
, m_pMark( m_pPoint )
, m_bIsInFrontOfLabel( false )
{
}
SwPaM::SwPaM( const SwPosition& rMark, const SwPosition& rPoint, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rMark )
, m_Bound2( rPoint )
, m_pPoint( &m_Bound2 )
, m_pMark( &m_Bound1 )
, m_bIsInFrontOfLabel( false )
{
}
SwPaM::SwPaM( const SwNodeIndex& rMark, const SwNodeIndex& rPoint,
long nMarkOffset, long nPointOffset, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rMark )
, m_Bound2( rPoint )
, m_pPoint( &m_Bound2 )
, m_pMark( &m_Bound1 )
, m_bIsInFrontOfLabel( false )
{
if ( nMarkOffset )
{
m_pMark->nNode += nMarkOffset;
}
if ( nPointOffset )
{
m_pPoint->nNode += nPointOffset;
}
m_Bound1.nContent.Assign( m_Bound1.nNode.GetNode().GetContentNode(), 0 );
m_Bound2.nContent.Assign( m_Bound2.nNode.GetNode().GetContentNode(), 0 );
}
SwPaM::SwPaM( const SwNode& rMark, const SwNode& rPoint,
long nMarkOffset, long nPointOffset, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rMark )
, m_Bound2( rPoint )
, m_pPoint( &m_Bound2 )
, m_pMark( &m_Bound1 )
, m_bIsInFrontOfLabel( false )
{
if ( nMarkOffset )
{
m_pMark->nNode += nMarkOffset;
}
if ( nPointOffset )
{
m_pPoint->nNode += nPointOffset;
}
m_Bound1.nContent.Assign( m_Bound1.nNode.GetNode().GetContentNode(), 0 );
m_Bound2.nContent.Assign( m_Bound2.nNode.GetNode().GetContentNode(), 0 );
}
SwPaM::SwPaM( const SwNodeIndex& rMark, sal_Int32 nMarkContent,
const SwNodeIndex& rPoint, sal_Int32 nPointContent, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rMark )
, m_Bound2( rPoint )
, m_pPoint( &m_Bound2 )
, m_pMark( &m_Bound1 )
, m_bIsInFrontOfLabel( false )
{
m_pPoint->nContent.Assign( rPoint.GetNode().GetContentNode(), nPointContent);
m_pMark ->nContent.Assign( rMark .GetNode().GetContentNode(), nMarkContent );
}
SwPaM::SwPaM( const SwNode& rMark, sal_Int32 nMarkContent,
const SwNode& rPoint, sal_Int32 nPointContent, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rMark )
, m_Bound2( rPoint )
, m_pPoint( &m_Bound2 )
, m_pMark( &m_Bound1 )
, m_bIsInFrontOfLabel( false )
{
m_pPoint->nContent.Assign( m_pPoint->nNode.GetNode().GetContentNode(),
nPointContent);
m_pMark ->nContent.Assign( m_pMark ->nNode.GetNode().GetContentNode(),
nMarkContent );
}
SwPaM::SwPaM( const SwNode& rNode, sal_Int32 nContent, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rNode )
, m_Bound2( m_Bound1.nNode.GetNode().GetNodes() ) // default initialize
, m_pPoint( &m_Bound1 )
, m_pMark( &m_Bound1 )
, m_bIsInFrontOfLabel( false )
{
m_pPoint->nContent.Assign( m_pPoint->nNode.GetNode().GetContentNode(),
nContent );
}
SwPaM::SwPaM( const SwNodeIndex& rNodeIdx, sal_Int32 nContent, SwPaM* pRing )
: Ring( pRing )
, m_Bound1( rNodeIdx )
, m_Bound2( rNodeIdx.GetNode().GetNodes() ) // default initialize
, m_pPoint( &m_Bound1 )
, m_pMark( &m_Bound1 )
, m_bIsInFrontOfLabel( false )
{
m_pPoint->nContent.Assign( rNodeIdx.GetNode().GetContentNode(), nContent );
}
SwPaM::~SwPaM() {}
SwPaM::SwPaM(SwPaM const& rPam, SwPaM *const pRing)
: Ring(pRing)
, m_Bound1( *(rPam.m_pPoint) )
, m_Bound2( *(rPam.m_pMark) )
, m_pPoint( &m_Bound1 ), m_pMark( rPam.HasMark() ? &m_Bound2 : m_pPoint )
, m_bIsInFrontOfLabel( false )
{
}
// @@@ semantic: no copy assignment for super class Ring.
SwPaM &SwPaM::operator=( const SwPaM &rPam )
{
*m_pPoint = *( rPam.m_pPoint );
if ( rPam.HasMark() )
{
SetMark();
*m_pMark = *( rPam.m_pMark );
}
else
{
DeleteMark();
}
return *this;
}
void SwPaM::SetMark()
{
if (m_pPoint == &m_Bound1)
{
m_pMark = &m_Bound2;
}
else
{
m_pMark = &m_Bound1;
}
(*m_pMark) = *m_pPoint;
}
#ifdef DBG_UTIL
void SwPaM::Exchange()
{
if (m_pPoint != m_pMark)
{
SwPosition *pTmp = m_pPoint;
m_pPoint = m_pMark;
m_pMark = pTmp;
}
}
#endif
/// movement of cursor
bool SwPaM::Move( SwMoveFnCollection const & fnMove, SwGoInDoc fnGo )
{
const bool bRet = (*fnGo)( *this, fnMove );
m_bIsInFrontOfLabel = false;
return bRet;
}
/** make a new region
Sets the first SwPaM onto the given SwPaM, or to the beginning or end of a
document. SPoint stays at its position, GetMark will be changed respectively.
@param fnMove Contains information if beginning or end of document.
@param pOrigRg The given region.
@return Newly created range, in Ring with parameter pOrigRg.
*/
std::unique_ptr<SwPaM> SwPaM::MakeRegion( SwMoveFnCollection const & fnMove, const SwPaM * pOrigRg )
{
std::unique_ptr<SwPaM> pPam;
if( pOrigRg == nullptr )
{
pPam.reset(new SwPaM( *m_pPoint ));
pPam->SetMark(); // set beginning
pPam->Move( fnMove, GoInSection); // to beginning or end of a node
// set SPoint onto its old position; set GetMark to the "end"
pPam->Exchange();
}
else
{
pPam.reset(new SwPaM(*pOrigRg, const_cast<SwPaM*>(pOrigRg))); // given search range
// make sure that SPoint is on the "real" start position
// FORWARD: SPoint always smaller than GetMark
// BACKWARD: SPoint always bigger than GetMark
if( (pPam->GetMark()->*fnMove.fnCmpOp)( *pPam->GetPoint() ) )
pPam->Exchange();
}
return pPam;
}
void SwPaM::Normalize(bool bPointFirst)
{
if (HasMark())
if ( ( bPointFirst && *m_pPoint > *m_pMark) ||
(!bPointFirst && *m_pPoint < *m_pMark) )
{
Exchange();
}
}
/// return page number at cursor (for reader and page bound frames)
sal_uInt16 SwPaM::GetPageNum( bool bAtPoint, const Point* pLayPos )
{
const SwContentFrame* pCFrame;
const SwPageFrame *pPg;
const SwContentNode *pNd ;
const SwPosition* pPos = bAtPoint ? m_pPoint : m_pMark;
if( nullptr != ( pNd = pPos->nNode.GetNode().GetContentNode() ) &&
nullptr != ( pCFrame = pNd->getLayoutFrame( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), pLayPos, pPos, false )) &&
nullptr != ( pPg = pCFrame->FindPageFrame() ))
return pPg->GetPhyPageNum();
return 0;
}
// form view - see also SwCursorShell::IsCursorReadonly()
static const SwFrame* lcl_FindEditInReadonlyFrame( const SwFrame& rFrame )
{
const SwFrame* pRet = nullptr;
const SwFlyFrame* pFly;
const SwSectionFrame* pSectionFrame;
if( rFrame.IsInFly() &&
(pFly = rFrame.FindFlyFrame())->GetFormat()->GetEditInReadonly().GetValue() &&
pFly->Lower() &&
!pFly->Lower()->IsNoTextFrame() )
{
pRet = pFly;
}
else if ( rFrame.IsInSct() &&
nullptr != ( pSectionFrame = rFrame.FindSctFrame() )->GetSection() &&
pSectionFrame->GetSection()->IsEditInReadonlyFlag() )
{
pRet = pSectionFrame;
}
return pRet;
}
/// is in protected section or selection surrounds something protected
bool SwPaM::HasReadonlySel( bool bFormView ) const
{
bool bRet = false;
const SwContentNode* pNd = GetPoint()->nNode.GetNode().GetContentNode();
const SwContentFrame *pFrame = nullptr;
if ( pNd != nullptr )
{
Point aTmpPt;
pFrame = pNd->getLayoutFrame( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), &aTmpPt, GetPoint(), false );
}
// Will be set if point are inside edit-in-readonly environment
const SwFrame* pPointEditInReadonlyFrame = nullptr;
if ( pFrame != nullptr
&& ( pFrame->IsProtected()
|| ( bFormView
&& nullptr == ( pPointEditInReadonlyFrame = lcl_FindEditInReadonlyFrame( *pFrame ) ) ) ) )
{
bRet = true;
}
else if( pNd != nullptr )
{
const SwSectionNode* pSNd = pNd->GetSectionNode();
if ( pSNd != nullptr
&& ( pSNd->GetSection().IsProtectFlag()
|| ( bFormView
&& !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
{
bRet = true;
}
}
if ( !bRet
&& HasMark()
&& GetPoint()->nNode != GetMark()->nNode )
{
pNd = GetMark()->nNode.GetNode().GetContentNode();
pFrame = nullptr;
if ( pNd != nullptr )
{
Point aTmpPt;
pFrame = pNd->getLayoutFrame( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), &aTmpPt, GetMark(), false );
}
const SwFrame* pMarkEditInReadonlyFrame = nullptr;
if ( pFrame != nullptr
&& ( pFrame->IsProtected()
|| ( bFormView
&& nullptr == ( pMarkEditInReadonlyFrame = lcl_FindEditInReadonlyFrame( *pFrame ) ) ) ) )
{
bRet = true;
}
else if( pNd != nullptr )
{
const SwSectionNode* pSNd = pNd->GetSectionNode();
if ( pSNd != nullptr
&& ( pSNd->GetSection().IsProtectFlag()
|| ( bFormView
&& !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
{
bRet = true;
}
}
if ( !bRet && bFormView )
{
// Check if start and end frame are inside the _same_
// edit-in-readonly-environment. Otherwise we better return 'true'
if ( pPointEditInReadonlyFrame != pMarkEditInReadonlyFrame )
bRet = true;
}
// check for protected section inside the selection
if( !bRet )
{
sal_uLong nSttIdx = GetMark()->nNode.GetIndex(),
nEndIdx = GetPoint()->nNode.GetIndex();
if( nEndIdx <= nSttIdx )
{
sal_uLong nTmp = nSttIdx;
nSttIdx = nEndIdx;
nEndIdx = nTmp;
}
// If a protected section should be between nodes, then the
// selection needs to contain already x nodes.
// (TextNd, SectNd, TextNd, EndNd, TextNd )
if( nSttIdx + 3 < nEndIdx )
{
const SwSectionFormats& rFormats = GetDoc()->GetSections();
for( SwSectionFormats::size_type n = rFormats.size(); n; )
{
const SwSectionFormat* pFormat = rFormats[ --n ];
if( pFormat->GetProtect().IsContentProtected() )
{
const SwFormatContent& rContent = pFormat->GetContent(false);
OSL_ENSURE( rContent.GetContentIdx(), "where is the SectionNode?" );
sal_uLong nIdx = rContent.GetContentIdx()->GetIndex();
if( nSttIdx <= nIdx && nEndIdx >= nIdx &&
rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes() )
{
bRet = true;
break;
}
}
}
}
}
}
const SwDoc *pDoc = GetDoc();
const IDocumentMarkAccess* pMarksAccess = pDoc->getIDocumentMarkAccess();
sw::mark::IFieldmark* pA = GetPoint() ? pMarksAccess->getFieldmarkFor( *GetPoint( ) ) : nullptr;
sw::mark::IFieldmark* pB = GetMark() ? pMarksAccess->getFieldmarkFor( *GetMark( ) ) : pA;
if (!bRet)
{
bool bUnhandledMark = pA && pA->GetFieldname( ) == ODF_UNHANDLED;
// Unhandled fieldmarks case shouldn't be edited manually to avoid breaking anything
if ( ( pA == pB ) && bUnhandledMark )
bRet = true;
else
{
bool bAtStartA = (pA != nullptr) && (pA->GetMarkStart() == *GetPoint());
bool bAtStartB = (pB != nullptr) && (pB->GetMarkStart() == *GetMark());
if ((pA == pB) && (bAtStartA != bAtStartB))
bRet = true;
else if (pA != pB)
{
// If both points are either outside or at marks edges (i.e. selection either
// touches fields, or fully encloses it), then don't disable editing
bRet = !( ( !pA || bAtStartA ) && ( !pB || bAtStartB ) );
}
if( !bRet && pDoc->GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM ) )
{
// Form protection case
bRet = ( pA == nullptr ) || ( pB == nullptr ) || bAtStartA || bAtStartB;
}
}
}
else
{
bRet = !( pA == pB && pA != nullptr );
}
if (!bRet)
{
// Paragraph Signatures and Classification fields are read-only.
if (pDoc && pDoc->GetEditShell())
bRet = pDoc->GetEditShell()->IsCursorInParagraphMetadataField();
}
return bRet;
}
/// This function returns the next node in direction of search. If there is no
/// left or the next is out of the area, then a null-pointer is returned.
/// @param rbFirst If <true> than first time request. If so than the position of
/// the PaM must not be changed!
SwContentNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFnCollection const & fnMove,
bool bInReadOnly )
{
SwContentNode * pNd = nullptr;
if( ((*rPam.GetPoint()).*fnMove.fnCmpOp)( *rPam.GetMark() ) ||
( *rPam.GetPoint() == *rPam.GetMark() && rbFirst ) )
{
SwContentFrame* pFrame;
if( rbFirst )
{
rbFirst = false;
pNd = rPam.GetContentNode();
if( pNd )
{
if(
(
nullptr == ( pFrame = pNd->getLayoutFrame( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() ) ) ||
( !bInReadOnly && pFrame->IsProtected() ) ||
(pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow())
) ||
( !bInReadOnly && pNd->FindSectionNode() &&
pNd->FindSectionNode()->GetSection().IsProtect()
)
)
{
pNd = nullptr;
}
}
}
if( !pNd ) // is the cursor not on a ContentNode?
{
SwPosition aPos( *rPam.GetPoint() );
bool bSrchForward = &fnMove == &fnMoveForward;
SwNodes& rNodes = aPos.nNode.GetNodes();
// go to next/previous ContentNode
while( true )
{
pNd = bSrchForward
? rNodes.GoNextSection( &aPos.nNode, true, !bInReadOnly )
: SwNodes::GoPrevSection( &aPos.nNode, true, !bInReadOnly );
if( pNd )
{
aPos.nContent.Assign( pNd, ::GetSttOrEnd( bSrchForward,*pNd ));
// is the position still in the area
if( (aPos.*fnMove.fnCmpOp)( *rPam.GetMark() ) )
{
// only in AutoTextSection can be nodes that are hidden
if( nullptr == ( pFrame = pNd->getLayoutFrame( pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() ) ) ||
( !bInReadOnly && pFrame->IsProtected() ) ||
( pFrame->IsTextFrame() &&
static_cast<SwTextFrame*>(pFrame)->IsHiddenNow() ) )
{
pNd = nullptr;
continue;
}
*rPam.GetPoint() = aPos;
}
else
pNd = nullptr; // no valid node
break;
}
break;
}
}
}
return pNd;
}
void GoStartDoc( SwPosition * pPos )
{
SwNodes& rNodes = pPos->nNode.GetNodes();
pPos->nNode = *rNodes.GetEndOfContent().StartOfSectionNode();
// we always need to find a ContentNode!
SwContentNode* pCNd = rNodes.GoNext( &pPos->nNode );
if( pCNd )
pCNd->MakeStartIndex( &pPos->nContent );
}
void GoEndDoc( SwPosition * pPos )
{
SwNodes& rNodes = pPos->nNode.GetNodes();
pPos->nNode = rNodes.GetEndOfContent();
SwContentNode* pCNd = GoPreviousNds( &pPos->nNode, true );
if( pCNd )
pCNd->MakeEndIndex( &pPos->nContent );
}
void GoStartSection( SwPosition * pPos )
{
// jump to section's beginning
SwNodes& rNodes = pPos->nNode.GetNodes();
sal_uInt16 nLevel = SwNodes::GetSectionLevel( pPos->nNode );
if( pPos->nNode < rNodes.GetEndOfContent().StartOfSectionIndex() )
nLevel--;
do { SwNodes::GoStartOfSection( &pPos->nNode ); } while( nLevel-- );
// already on a ContentNode
pPos->nNode.GetNode().GetContentNode()->MakeStartIndex( &pPos->nContent );
}
/// go to the end of the current base section
void GoEndSection( SwPosition * pPos )
{
// jump to section's beginning/end
SwNodes& rNodes = pPos->nNode.GetNodes();
sal_uInt16 nLevel = SwNodes::GetSectionLevel( pPos->nNode );
if( pPos->nNode < rNodes.GetEndOfContent().StartOfSectionIndex() )
nLevel--;
do { SwNodes::GoEndOfSection( &pPos->nNode ); } while( nLevel-- );
// now on a EndNode, thus to the previous ContentNode
if( GoPreviousNds( &pPos->nNode, true ) )
pPos->nNode.GetNode().GetContentNode()->MakeEndIndex( &pPos->nContent );
}
bool GoInDoc( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
(*fnMove.fnDoc)( rPam.GetPoint() );
return true;
}
bool GoInSection( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
(*fnMove.fnSections)( rPam.GetPoint() );
return true;
}
bool GoInNode( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
SwContentNode *pNd = (*fnMove.fnNds)( &rPam.GetPoint()->nNode, true );
if( pNd )
rPam.GetPoint()->nContent.Assign( pNd,
::GetSttOrEnd( &fnMove == &fnMoveForward, *pNd ) );
return pNd;
}
bool GoInContent( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
&rPam.GetPoint()->nContent, CRSR_SKIP_CHARS ))
return true;
return GoInNode( rPam, fnMove );
}
bool GoInContentCells( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
&rPam.GetPoint()->nContent, CRSR_SKIP_CELLS ))
return true;
return GoInNode( rPam, fnMove );
}
bool GoInContentSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
&rPam.GetPoint()->nContent, CRSR_SKIP_CHARS | CRSR_SKIP_HIDDEN ) )
return true;
return GoInNode( rPam, fnMove );
}
bool GoInContentCellsSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
&rPam.GetPoint()->nContent, CRSR_SKIP_CELLS | CRSR_SKIP_HIDDEN ) )
return true;
return GoInNode( rPam, fnMove );
}
bool GoPrevPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
{
if( rPam.Move( fnMoveBackward, GoInNode ) )
{
// always on a ContentNode
SwPosition& rPos = *rPam.GetPoint();
SwContentNode * pNd = rPos.nNode.GetNode().GetContentNode();
rPos.nContent.Assign( pNd,
::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ) );
return true;
}
return false;
}
bool GoCurrPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
{
SwPosition& rPos = *rPam.GetPoint();
SwContentNode * pNd = rPos.nNode.GetNode().GetContentNode();
if( pNd )
{
const sal_Int32 nOld = rPos.nContent.GetIndex();
const sal_Int32 nNew = &aPosPara == &fnMoveForward ? 0 : pNd->Len();
// if already at beginning/end then to the next/previous
if( nOld != nNew )
{
rPos.nContent.Assign( pNd, nNew );
return true;
}
}
// move node to next/previous ContentNode
if( ( &aPosPara==&fnParaStart && nullptr != ( pNd =
GoPreviousNds( &rPos.nNode, true ))) ||
( &aPosPara==&fnParaEnd && nullptr != ( pNd =
GoNextNds( &rPos.nNode, true ))) )
{
rPos.nContent.Assign( pNd,
::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ));
return true;
}
return false;
}
bool GoNextPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
{
if( rPam.Move( fnMoveForward, GoInNode ) )
{
// always on a ContentNode
SwPosition& rPos = *rPam.GetPoint();
SwContentNode * pNd = rPos.nNode.GetNode().GetContentNode();
rPos.nContent.Assign( pNd,
::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ) );
return true;
}
return false;
}
bool GoCurrSection( SwPaM & rPam, SwMoveFnCollection const & fnMove )
{
SwPosition& rPos = *rPam.GetPoint();
SwPosition aSavePos( rPos ); // position for comparison
(fnMove.fnSection)( &rPos.nNode );
SwContentNode *pNd;
if( nullptr == ( pNd = rPos.nNode.GetNode().GetContentNode()) &&
nullptr == ( pNd = (*fnMove.fnNds)( &rPos.nNode, true )) )
{
rPos = aSavePos; // do not change cursor
return false;
}
rPos.nContent.Assign( pNd,
::GetSttOrEnd( &fnMove == &fnMoveForward, *pNd ) );
return aSavePos != rPos;
}
OUString SwPaM::GetText() const
{
OUStringBuffer aResult;
SwNodeIndex aNodeIndex = Start()->nNode;
// The first node can be already the end node.
// Use a "forever" loop with an exit condition in the middle
// of its body, in order to correctly handle all cases.
bool bIsStartNode = true;
for (;;)
{
const bool bIsEndNode = aNodeIndex == End()->nNode;
SwTextNode * pTextNode = aNodeIndex.GetNode().GetTextNode();
if (pTextNode != nullptr)
{
const OUString aTmpStr = pTextNode->GetText();
if (bIsStartNode || bIsEndNode)
{
// Handle corner cases of start/end node(s)
const sal_Int32 nStart = bIsStartNode
? Start()->nContent.GetIndex()
: 0;
const sal_Int32 nEnd = bIsEndNode
? End()->nContent.GetIndex()
: aTmpStr.getLength();
aResult.appendCopy(aTmpStr, nStart, nEnd-nStart);
}
else
{
aResult.append(aTmpStr);
}
}
if (bIsEndNode)
{
break;
}
++aNodeIndex;
bIsStartNode = false;
}
return aResult.makeStringAndClear();
}
void SwPaM::InvalidatePaM()
{
const SwNode &_pNd = GetNode();
const SwTextNode *_pTextNd = _pNd.GetTextNode();
if (_pTextNd != nullptr)
{
// pretend that the PaM marks inserted text to recalc the portion...
SwInsText aHint( Start()->nContent.GetIndex(),
End()->nContent.GetIndex() - Start()->nContent.GetIndex() + 1 );
SwModify *_pModify=const_cast<SwModify*>(static_cast<SwModify const *>(_pTextNd));
_pModify->ModifyNotification( nullptr, &aHint);
}
}
void SwPaM::dumpAsXml(xmlTextWriterPtr pWriter) const
{
xmlTextWriterStartElement(pWriter, BAD_CAST("SwPaM"));
xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
GetPoint()->dumpAsXml(pWriter);
xmlTextWriterEndElement(pWriter);
if (HasMark())
{
xmlTextWriterStartElement(pWriter, BAD_CAST("mark"));
GetMark()->dumpAsXml(pWriter);
xmlTextWriterEndElement(pWriter);
}
xmlTextWriterEndElement(pWriter);
}
std::ostream &operator <<(std::ostream& s, const SwPaM& pam)
{
if( pam.HasMark())
return s << "SwPaM (point " << *pam.GetPoint() << ", mark " << *pam.GetMark() << ")";
else
return s << "SwPaM (point " << *pam.GetPoint() << ")";
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V595 The 'pDoc' pointer was utilized before it was verified against nullptr. Check lines: 704, 719.