1410 lines
45 KiB
C++
1410 lines
45 KiB
C++
/* -*- 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 <sal/config.h>
|
|
|
|
#include <tools/gen.hxx>
|
|
#include <editeng/protitem.hxx>
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <unotools/configmgr.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 <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 <txatbase.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <utility>
|
|
#include <xmloff/odffields.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
|
|
#include <editsh.hxx>
|
|
#include <textcontentcontrol.hxx>
|
|
|
|
// for the dump "MSC-" compiler
|
|
static sal_Int32 GetSttOrEnd( bool bCondition, const SwContentNode& rNd )
|
|
{
|
|
return bCondition ? 0 : rNd.Len();
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, const SwContentIndex & rContent )
|
|
: nNode( rNodeIndex ), nContent( rContent )
|
|
{
|
|
assert((!rNodeIndex.GetNode().GetContentNode() || rNodeIndex.GetNode().GetContentNode() == rContent.GetContentNode())
|
|
&& "parameters point to different nodes");
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwNode & rNode, const SwContentIndex & rContent )
|
|
: nNode( rNode ), nContent( rContent )
|
|
{
|
|
assert((!rNode.GetContentNode() || rNode.GetContentNode() == rContent.GetContentNode())
|
|
&& "parameters point to different nodes");
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, const SwContentNode* pContentNode, sal_Int32 nContentOffset )
|
|
: nNode( rNodeIndex ), nContent( pContentNode, nContentOffset )
|
|
{
|
|
assert((!pContentNode || pContentNode == &rNodeIndex.GetNode()) &&
|
|
"parameters point to different nodes");
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwNode & rNode, const SwContentNode* pContentNode, sal_Int32 nContentOffset )
|
|
: nNode( rNode ), nContent( pContentNode, nContentOffset )
|
|
{
|
|
assert((!pContentNode || pContentNode == &rNode) &&
|
|
"parameters point to different nodes");
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, SwNodeOffset nDiff, const SwContentNode* pContentNode, sal_Int32 nContentOffset )
|
|
: nNode( rNodeIndex, nDiff ), nContent( pContentNode, nContentOffset )
|
|
{
|
|
assert((!pContentNode || pContentNode == &rNodeIndex.GetNode()) &&
|
|
"parameters point to different nodes");
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, SwNodeOffset nDiff )
|
|
: nNode( rNodeIndex, nDiff ), nContent( GetNode().GetContentNode() )
|
|
{
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwNode& rNode, SwNodeOffset nDiff )
|
|
: nNode( rNode, nDiff ), nContent( GetNode().GetContentNode() )
|
|
{
|
|
}
|
|
|
|
SwPosition::SwPosition( SwNodes& rNodes, SwNodeOffset nIndex )
|
|
: nNode( rNodes, nIndex ), nContent( GetNode().GetContentNode() )
|
|
{
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwContentNode & rNode, const sal_Int32 nContentOffset )
|
|
: nNode( rNode ), nContent( &rNode, nContentOffset )
|
|
{
|
|
}
|
|
|
|
SwPosition::SwPosition( const SwContentIndex & rContentIndex, short nDiff )
|
|
: nNode( *rContentIndex.GetContentNode() ), nContent( rContentIndex, nDiff )
|
|
{
|
|
}
|
|
|
|
bool SwPosition::operator<(const SwPosition &rPos) const
|
|
{
|
|
// cheaper to check for == first
|
|
if( nNode == rPos.nNode )
|
|
{
|
|
// note that positions with text node but no SwContentIndex registered are
|
|
// created for text frames anchored at para (see SwXFrame::getAnchor())
|
|
SwContentNode const*const pThisReg(GetContentNode());
|
|
SwContentNode const*const pOtherReg(rPos.GetContentNode());
|
|
if (pThisReg && pOtherReg)
|
|
{
|
|
return (nContent < rPos.nContent);
|
|
}
|
|
else // by convention position with no index is smaller
|
|
{
|
|
return pOtherReg != nullptr;
|
|
}
|
|
}
|
|
return nNode < rPos.nNode;
|
|
}
|
|
|
|
bool SwPosition::operator>(const SwPosition &rPos) const
|
|
{
|
|
// cheaper to check for == first
|
|
if( nNode == rPos.nNode )
|
|
{
|
|
// note that positions with text node but no SwContentIndex registered are
|
|
// created for text frames anchored at para (see SwXFrame::getAnchor())
|
|
SwContentNode const*const pThisReg(GetContentNode());
|
|
SwContentNode const*const pOtherReg(rPos.GetContentNode());
|
|
if (pThisReg && pOtherReg)
|
|
{
|
|
return (nContent > rPos.nContent);
|
|
}
|
|
else // by convention position with no index is smaller
|
|
{
|
|
return pThisReg != nullptr;
|
|
}
|
|
}
|
|
return nNode > rPos.nNode;
|
|
}
|
|
|
|
bool SwPosition::operator<=(const SwPosition &rPos) const
|
|
{
|
|
// cheaper to check for == first
|
|
if( nNode == rPos.nNode )
|
|
{
|
|
// note that positions with text node but no SwContentIndex registered are
|
|
// created for text frames anchored at para (see SwXFrame::getAnchor())
|
|
SwContentNode const*const pThisReg(GetContentNode());
|
|
SwContentNode const*const pOtherReg(rPos.GetContentNode());
|
|
if (pThisReg && pOtherReg)
|
|
{
|
|
return (nContent <= rPos.nContent);
|
|
}
|
|
else // by convention position with no index is smaller
|
|
{
|
|
return pThisReg == nullptr;
|
|
}
|
|
}
|
|
return nNode < rPos.nNode;
|
|
}
|
|
|
|
bool SwPosition::operator>=(const SwPosition &rPos) const
|
|
{
|
|
// cheaper to check for == first
|
|
if( nNode == rPos.nNode )
|
|
{
|
|
// note that positions with text node but no SwContentIndex registered are
|
|
// created for text frames anchored at para (see SwXFrame::getAnchor())
|
|
SwContentNode const*const pThisReg(GetContentNode());
|
|
SwContentNode const*const pOtherReg(rPos.GetContentNode());
|
|
if (pThisReg && pOtherReg)
|
|
{
|
|
return (nContent >= rPos.nContent);
|
|
}
|
|
else // by convention position with no index is smaller
|
|
{
|
|
return pOtherReg == nullptr;
|
|
}
|
|
}
|
|
return nNode > rPos.nNode;
|
|
}
|
|
|
|
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 GetNode().GetDoc();
|
|
}
|
|
|
|
void SwPosition::dumpAsXml(xmlTextWriterPtr pWriter) const
|
|
{
|
|
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPosition"));
|
|
(void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nNode"), BAD_CAST(OString::number(sal_Int32(GetNodeIndex())).getStr()));
|
|
(void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nContent"), BAD_CAST(OString::number(GetContentIndex()).getStr()));
|
|
(void)xmlTextWriterEndElement(pWriter);
|
|
}
|
|
|
|
void SwPosition::Assign( const SwNode& rNd, SwNodeOffset nDelta, sal_Int32 nContentOffset )
|
|
{
|
|
nNode.Assign(rNd, nDelta);
|
|
assert((nNode.GetNode().GetContentNode() || nContentOffset == 0) && "setting contentoffset, but node is not SwContentNode");
|
|
nContent.Assign(nNode.GetNode().GetContentNode(), nContentOffset);
|
|
}
|
|
void SwPosition::Assign( SwNodeOffset nNodeOffset, sal_Int32 nContentOffset )
|
|
{
|
|
nNode = nNodeOffset;
|
|
nContent.Assign(nNode.GetNode().GetContentNode(), nContentOffset);
|
|
}
|
|
void SwPosition::Assign( const SwContentNode& rNode, sal_Int32 nContentOffset )
|
|
{
|
|
nNode = rNode;
|
|
nContent.Assign(&rNode, nContentOffset);
|
|
}
|
|
void SwPosition::Assign( const SwNode& rNd, sal_Int32 nContentOffset )
|
|
{
|
|
nNode.Assign(rNd);
|
|
nContent.Assign(rNd.GetContentNode(), nContentOffset);
|
|
}
|
|
void SwPosition::Assign( const SwNodeIndex& rNdIdx, sal_Int32 nContentOffset )
|
|
{
|
|
nNode = rNdIdx;
|
|
nContent.Assign(nNode.GetNode().GetContentNode(), nContentOffset);
|
|
}
|
|
void SwPosition::Adjust( SwNodeOffset nDelta )
|
|
{
|
|
nNode += nDelta;
|
|
nContent.Assign(nNode.GetNode().GetContentNode(), 0);
|
|
}
|
|
void SwPosition::AdjustContent( sal_Int32 nDelta )
|
|
{
|
|
assert(nNode.GetNode().GetContentNode() && "only valid to call this if we point to an SwContentNode");
|
|
nContent += nDelta;
|
|
}
|
|
void SwPosition::SetContent( sal_Int32 nContentIndex )
|
|
{
|
|
assert(nNode.GetNode().GetContentNode() && "only valid to call this if we point to an SwContentNode");
|
|
nContent = nContentIndex;
|
|
}
|
|
void SwPosition::AssignStartIndex( const SwContentNode& rNd )
|
|
{
|
|
nNode = rNd;
|
|
nContent.Assign(&rNd, 0);
|
|
}
|
|
void SwPosition::AssignEndIndex( const SwContentNode& rNd )
|
|
{
|
|
nNode = rNd;
|
|
nContent.Assign(&rNd, rNd.Len());
|
|
}
|
|
|
|
|
|
std::ostream &operator <<(std::ostream& s, const SwPosition& position)
|
|
{
|
|
return s << "SwPosition (node " << position.GetNodeIndex() << ", offset " << position.GetContentIndex() << ")";
|
|
}
|
|
|
|
namespace {
|
|
|
|
enum CHKSECTION { Chk_Both, Chk_One, Chk_None };
|
|
|
|
}
|
|
|
|
static CHKSECTION lcl_TstIdx( SwNodeOffset nSttIdx, SwNodeOffset nEndIdx, const SwNode& rEndNd )
|
|
{
|
|
SwNodeOffset 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, SwNodeOffset nStt, SwNodeOffset 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;
|
|
|
|
for (;;)
|
|
{
|
|
pTmp = pNd->StartOfSectionNode();
|
|
if (pTmp->EndOfSectionNode() == &rBaseEnd )
|
|
break;
|
|
pNd = pTmp;
|
|
}
|
|
|
|
SwNodeOffset nSttIdx = pNd->GetIndex(), nEndIdx = pNd->EndOfSectionIndex();
|
|
return nSttIdx <= nStt && nStt <= nEndIdx &&
|
|
nSttIdx <= nEnd && nEnd <= nEndIdx;
|
|
}
|
|
|
|
/** Check if the given range is inside one of the defined top-level sections.
|
|
*
|
|
* The top-level sections are Content, AutoText, PostIts, Inserts, and Redlines.
|
|
*
|
|
* @param bChkSection if true, also check that the given range is inside
|
|
* a single second-level section inside any of the
|
|
* top-level sections, except for the Content section.
|
|
*
|
|
* @return <true> if valid range
|
|
*/
|
|
bool CheckNodesRange( const SwNode& rStt,
|
|
const SwNode& rEnd, bool bChkSection )
|
|
{
|
|
const SwNodes& rNds = rStt.GetNodes();
|
|
SwNodeOffset 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, SwContentIndex * pIdx, SwCursorSkipMode nMode )
|
|
{
|
|
if( pNd->IsContentNode() )
|
|
return static_cast<SwContentNode*>(pNd)->GoNext( pIdx, nMode );
|
|
return false;
|
|
}
|
|
|
|
bool GoPrevious( SwNode* pNd, SwContentIndex * pIdx, SwCursorSkipMode 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 = SwNodes::GoNext(&aIdx);
|
|
if( pNd )
|
|
{
|
|
if( bChk && SwNodeOffset(1) != aIdx.GetIndex() - pIdx->GetIndex() &&
|
|
!CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), 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 && SwNodeOffset(1) != pIdx->GetIndex() - aIdx.GetIndex() &&
|
|
!CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), true ) )
|
|
pNd = nullptr;
|
|
else
|
|
*pIdx = aIdx;
|
|
}
|
|
return pNd;
|
|
}
|
|
|
|
SwContentNode* GoNextPos( SwPosition* pIdx, bool bChk )
|
|
{
|
|
SwNodeIndex aIdx( pIdx->GetNode() );
|
|
SwContentNode* pNd = SwNodes::GoNext(&aIdx);
|
|
if( pNd )
|
|
{
|
|
if( bChk && SwNodeOffset(1) != aIdx.GetIndex() - pIdx->GetNodeIndex() &&
|
|
!CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), true ) )
|
|
pNd = nullptr;
|
|
else
|
|
pIdx->Assign(aIdx);
|
|
}
|
|
return pNd;
|
|
}
|
|
|
|
SwContentNode* GoPreviousPos( SwPosition * pIdx, bool bChk )
|
|
{
|
|
SwNodeIndex aIdx( pIdx->GetNode() );
|
|
SwContentNode* pNd = SwNodes::GoPrevious( &aIdx );
|
|
if( pNd )
|
|
{
|
|
if( bChk && SwNodeOffset(1) != pIdx->GetNodeIndex() - aIdx.GetIndex() &&
|
|
!CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), true ) )
|
|
pNd = nullptr;
|
|
else
|
|
pIdx->Assign(aIdx);
|
|
}
|
|
return pNd;
|
|
}
|
|
|
|
SwPaM::SwPaM( const SwPosition& rPos, SwPaM* pRing )
|
|
: Ring( pRing )
|
|
, m_Bound1( rPos )
|
|
, m_Bound2( rPos.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,
|
|
SwNodeOffset nMarkOffset, SwNodeOffset 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.GetNode().GetContentNode(), 0 );
|
|
m_Bound2.nContent.Assign( m_Bound2.GetNode().GetContentNode(), 0 );
|
|
}
|
|
|
|
SwPaM::SwPaM( const SwNode& rMark, const SwNode& rPoint,
|
|
SwNodeOffset nMarkOffset, SwNodeOffset 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.GetNode().GetContentNode(), 0 );
|
|
m_Bound2.nContent.Assign( m_Bound2.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->GetNode().GetContentNode(),
|
|
nPointContent);
|
|
m_pMark ->nContent.Assign( m_pMark ->GetNode().GetContentNode(),
|
|
nMarkContent );
|
|
}
|
|
|
|
SwPaM::SwPaM( const SwNode& rMark, SwNodeOffset nMarkOffset, sal_Int32 nMarkContent,
|
|
const SwNode& rPoint, SwNodeOffset nPointOffset, sal_Int32 nPointContent, 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_pPoint->nContent.Assign( m_pPoint->GetNode().GetContentNode(),
|
|
nPointContent);
|
|
m_pMark ->nContent.Assign( m_pMark ->GetNode().GetContentNode(),
|
|
nMarkContent );
|
|
}
|
|
|
|
SwPaM::SwPaM( const SwNode& rNode, sal_Int32 nContent, SwPaM* pRing )
|
|
: Ring( pRing )
|
|
, m_Bound1( rNode )
|
|
, m_Bound2( m_Bound1.GetNode().GetNodes() ) // default initialize
|
|
, m_pPoint( &m_Bound1 )
|
|
, m_pMark( &m_Bound1 )
|
|
, m_bIsInFrontOfLabel( false )
|
|
{
|
|
m_pPoint->nContent.Assign( m_pPoint->GetNode().GetContentNode(),
|
|
nContent );
|
|
}
|
|
|
|
SwPaM::SwPaM( const SwNode& rNode, SwNodeOffset nNdOffset, sal_Int32 nContent, SwPaM* pRing )
|
|
: Ring( pRing )
|
|
, m_Bound1( rNode, nNdOffset )
|
|
, m_Bound2( m_Bound1.GetNode().GetNodes() ) // default initialize
|
|
, m_pPoint( &m_Bound1 )
|
|
, m_pMark( &m_Bound1 )
|
|
, m_bIsInFrontOfLabel( false )
|
|
{
|
|
m_pPoint->nContent.Assign( m_pPoint->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( SwNodes& rNodes, SwNodeOffset nNdOffset, SwPaM* pRing )
|
|
: Ring( pRing )
|
|
, m_Bound1( rNodes, nNdOffset )
|
|
, m_Bound2( rNodes ) // default initialize
|
|
, m_pPoint( &m_Bound1 )
|
|
, m_pMark( &m_Bound1 )
|
|
, m_bIsInFrontOfLabel( false )
|
|
{
|
|
}
|
|
|
|
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 )
|
|
{
|
|
if(this == &rPam)
|
|
return *this;
|
|
|
|
*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;
|
|
}
|
|
|
|
/// movement of cursor
|
|
bool SwPaM::Move( SwMoveFnCollection const & fnMove, SwGoInDoc fnGo )
|
|
{
|
|
const bool bRet = (*fnGo)( *this, fnMove );
|
|
|
|
m_bIsInFrontOfLabel = false;
|
|
return bRet;
|
|
}
|
|
|
|
namespace sw {
|
|
|
|
/** 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.
|
|
@param rPam returns newly created range, in Ring with parameter pOrigRg.
|
|
*/
|
|
void MakeRegion(SwMoveFnCollection const & fnMove,
|
|
const SwPaM & rOrigRg, std::optional<SwPaM>& rPam)
|
|
{
|
|
rPam.emplace(rOrigRg, const_cast<SwPaM*>(&rOrigRg)); // 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( (rPam->GetMark()->*fnMove.fnCmpOp)( *rPam->GetPoint() ) )
|
|
rPam->Exchange();
|
|
}
|
|
|
|
} // namespace sw
|
|
|
|
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;
|
|
|
|
std::pair<Point, bool> tmp;
|
|
if (pLayPos)
|
|
{
|
|
tmp.first = *pLayPos;
|
|
tmp.second = false;
|
|
}
|
|
if( nullptr != ( pNd = pPos->GetNode().GetContentNode() ) &&
|
|
nullptr != (pCFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), pPos, pLayPos ? &tmp : nullptr)) &&
|
|
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, bool const isReplace) const
|
|
{
|
|
bool bRet = false;
|
|
|
|
const SwContentNode* pNd = GetPoint()->GetNode().GetContentNode();
|
|
const SwContentFrame *pFrame = nullptr;
|
|
if ( pNd != nullptr )
|
|
{
|
|
Point aTmpPt;
|
|
std::pair<Point, bool> const tmp(aTmpPt, false);
|
|
pFrame = pNd->getLayoutFrame(
|
|
pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
|
|
GetPoint(), &tmp);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
else
|
|
{
|
|
const SwSectionNode* pParentSectionNd = pNd->FindSectionNode();
|
|
if ( pParentSectionNd != nullptr
|
|
&& ( pParentSectionNd->GetSection().IsProtectFlag()
|
|
|| ( bFormView && !pParentSectionNd->GetSection().IsEditInReadonlyFlag()) ) )
|
|
{
|
|
bRet = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bRet
|
|
&& HasMark()
|
|
&& GetPoint()->nNode != GetMark()->nNode )
|
|
{
|
|
pNd = GetMark()->GetNode().GetContentNode();
|
|
pFrame = nullptr;
|
|
if ( pNd != nullptr )
|
|
{
|
|
Point aTmpPt;
|
|
std::pair<Point, bool> const tmp(aTmpPt, false);
|
|
pFrame = pNd->getLayoutFrame(
|
|
pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
|
|
GetMark(), &tmp);
|
|
}
|
|
|
|
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 )
|
|
{
|
|
SwNodeOffset nSttIdx = GetMark()->GetNodeIndex(),
|
|
nEndIdx = GetPoint()->GetNodeIndex();
|
|
if( nEndIdx < nSttIdx )
|
|
std::swap( nSttIdx, nEndIdx );
|
|
|
|
// If a protected section should be between nodes, then the
|
|
// selection needs to contain already x nodes.
|
|
// (TextNd, SectNd, TextNd, EndNd, TextNd )
|
|
if( nSttIdx + SwNodeOffset(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?" );
|
|
SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex();
|
|
if( nSttIdx <= nIdx && nEndIdx >= nIdx &&
|
|
rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes() )
|
|
{
|
|
bRet = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const SwDoc& rDoc = GetDoc();
|
|
// Legacy text/combo/checkbox: never return read-only when inside these form fields.
|
|
const IDocumentMarkAccess* pMarksAccess = rDoc.getIDocumentMarkAccess();
|
|
sw::mark::Fieldmark* pA = GetPoint() ? pMarksAccess->getInnerFieldmarkFor(*GetPoint()) : nullptr;
|
|
sw::mark::Fieldmark* pB = GetMark() ? pMarksAccess->getInnerFieldmarkFor(*GetMark()) : pA;
|
|
// prevent the user from accidentally deleting the field itself when modifying the text.
|
|
const bool bAtStartA = (pA != nullptr) && (pA->GetMarkStart() == *GetPoint());
|
|
const bool bAtStartB = (pB != nullptr) && (pB->GetMarkStart() == *GetMark());
|
|
|
|
if (officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get())
|
|
{
|
|
; // allow editing all fields in generic mode
|
|
}
|
|
else 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
|
|
{
|
|
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 && rDoc.GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM ) && (pA || pB) )
|
|
{
|
|
// Form protection case
|
|
bRet = ( pA == nullptr ) || ( pB == nullptr ) || bAtStartA || bAtStartB;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Allow editing when the cursor/selection is fully inside of a legacy form field.
|
|
bRet = !( pA != nullptr && !bAtStartA && !bAtStartB && pA == pB );
|
|
|
|
if (bRet)
|
|
{
|
|
// Also allow editing inside content controls in general, similar to form fields.
|
|
// Specific types will be disabled below.
|
|
if (const SwEditShell* pEditShell = rDoc.GetEditShell())
|
|
bRet = !pEditShell->CursorInsideContentControl();
|
|
}
|
|
}
|
|
|
|
if (!bRet)
|
|
{
|
|
// Paragraph Signatures and Classification fields are read-only.
|
|
if (const SwEditShell* pEditShell = rDoc.GetEditShell())
|
|
bRet = pEditShell->IsCursorInParagraphMetadataField();
|
|
}
|
|
|
|
if (!bRet &&
|
|
rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS))
|
|
{
|
|
if (rDoc.getIDocumentMarkAccess()->isBookmarkDeleted(*this, isReplace))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if (!bRet &&
|
|
rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FIELDS))
|
|
{
|
|
SwPosition const& rStart(*Start());
|
|
SwPosition const& rEnd(*End());
|
|
for (SwNodeIndex n(rStart.GetNode()); n <= rEnd.GetNode(); ++n)
|
|
{
|
|
if (SwTextNode const*const pNode = n.GetNode().GetTextNode())
|
|
{
|
|
if (SwpHints const*const pHints = pNode->GetpSwpHints())
|
|
{
|
|
for (size_t i = 0; i < pHints->Count(); ++i)
|
|
{
|
|
SwTextAttr const*const pHint(pHints->Get(i));
|
|
if (n == rStart.GetNode() && pHint->GetStart() < rStart.GetContentIndex())
|
|
{
|
|
continue; // before selection
|
|
}
|
|
if (n == rEnd.GetNode() && rEnd.GetContentIndex() <= pHint->GetStart())
|
|
{
|
|
break; // after selection
|
|
}
|
|
if (pHint->Which() == RES_TXTATR_FIELD
|
|
// placeholders don't work if you can't delete them
|
|
&& pHint->GetFormatField().GetField()->GetTyp()->Which() != SwFieldIds::JumpEdit)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bRet)
|
|
{
|
|
// See if we're inside a read-only content control.
|
|
const SwPosition* pStart = Start();
|
|
SwTextNode* pTextNode = pStart->GetNode().GetTextNode();
|
|
if (pTextNode)
|
|
{
|
|
sal_Int32 nIndex = pStart->GetContentIndex();
|
|
SwTextAttr* pAttr
|
|
= pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent);
|
|
auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
|
|
if (pTextContentControl)
|
|
{
|
|
const SwFormatContentControl& rFormatContentControl
|
|
= pTextContentControl->GetContentControl();
|
|
std::shared_ptr<SwContentControl> pContentControl
|
|
= rFormatContentControl.GetContentControl();
|
|
if (pContentControl && !pContentControl->GetReadWrite())
|
|
{
|
|
switch (pContentControl->GetType())
|
|
{
|
|
case SwContentControlType::CHECKBOX:
|
|
case SwContentControlType::PICTURE:
|
|
case SwContentControlType::DROP_DOWN_LIST:
|
|
bRet = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
bool SwPaM::HasHiddenSections() const
|
|
{
|
|
bool bRet = false;
|
|
|
|
if (HasMark() && GetPoint()->nNode != GetMark()->nNode)
|
|
{
|
|
// check for hidden section inside the selection
|
|
SwNodeOffset nSttIdx = Start()->GetNodeIndex(), nEndIdx = End()->GetNodeIndex();
|
|
|
|
if (nSttIdx + SwNodeOffset(3) < nEndIdx)
|
|
{
|
|
const SwSectionFormats& rFormats = GetDoc().GetSections();
|
|
for (SwSectionFormats::size_type n = rFormats.size(); n;)
|
|
{
|
|
const SwSectionFormat* pFormat = rFormats[--n];
|
|
if (pFormat->GetSection()->IsHidden())
|
|
{
|
|
const SwFormatContent& rContent = pFormat->GetContent(false);
|
|
OSL_ENSURE(rContent.GetContentIdx(), "where is the SectionNode?");
|
|
SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex();
|
|
if (nSttIdx <= nIdx && nEndIdx >= nIdx
|
|
&& rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes())
|
|
{
|
|
bRet = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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> then 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 const bInReadOnly, SwRootFrame const*const i_pLayout)
|
|
{
|
|
SwRootFrame const*const pLayout(i_pLayout ? i_pLayout :
|
|
rPam.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout());
|
|
SwContentNode * pNd = nullptr;
|
|
if( ((*rPam.GetPoint()).*fnMove.fnCmpOp)( *rPam.GetMark() ) ||
|
|
( *rPam.GetPoint() == *rPam.GetMark() && rbFirst ) )
|
|
{
|
|
if( rbFirst )
|
|
{
|
|
rbFirst = false;
|
|
pNd = rPam.GetPointContentNode();
|
|
if( pNd )
|
|
{
|
|
SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
|
|
if(
|
|
(
|
|
nullptr == pFrame ||
|
|
( !bInReadOnly && pFrame->IsProtected() ) ||
|
|
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;
|
|
|
|
// go to next/previous ContentNode
|
|
while( true )
|
|
{
|
|
if (i_pLayout && aPos.GetNode().IsTextNode())
|
|
{
|
|
auto const fal(sw::GetFirstAndLastNode(*pLayout, aPos.GetNode()));
|
|
aPos.Assign( bSrchForward ? *fal.second : *fal.first );
|
|
}
|
|
|
|
pNd = bSrchForward
|
|
? SwNodes::GoNextSection( &aPos, true, !bInReadOnly )
|
|
: SwNodes::GoPrevSection( &aPos, true, !bInReadOnly );
|
|
if( pNd )
|
|
{
|
|
if (!bSrchForward)
|
|
aPos.AssignEndIndex( *pNd );
|
|
// is the position still in the area
|
|
if( (aPos.*fnMove.fnCmpOp)( *rPam.GetMark() ) )
|
|
{
|
|
// only in AutoTextSection can be nodes that are hidden
|
|
SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
|
|
if (nullptr == pFrame ||
|
|
( !bInReadOnly && pFrame->IsProtected() ) ||
|
|
pFrame->IsHiddenNow())
|
|
{
|
|
continue;
|
|
}
|
|
*rPam.GetPoint() = aPos;
|
|
}
|
|
else
|
|
pNd = nullptr; // no valid node
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pNd;
|
|
}
|
|
|
|
void GoStartDoc( SwPosition * pPos )
|
|
{
|
|
SwNodes& rNodes = pPos->GetNodes();
|
|
pPos->Assign( *rNodes.GetEndOfContent().StartOfSectionNode() );
|
|
// we always need to find a ContentNode!
|
|
SwNodes::GoNext(pPos);
|
|
}
|
|
|
|
void GoEndDoc( SwPosition * pPos )
|
|
{
|
|
SwNodes& rNodes = pPos->GetNodes();
|
|
pPos->Assign( rNodes.GetEndOfContent() );
|
|
SwContentNode* pCNd = GoPreviousPos( pPos, true );
|
|
if( pCNd )
|
|
pPos->AssignEndIndex(*pCNd);
|
|
}
|
|
|
|
void GoStartSection( SwPosition * pPos )
|
|
{
|
|
// jump to section's beginning
|
|
SwNodes& rNodes = pPos->GetNodes();
|
|
int nLevel = SwNodes::GetSectionLevel( pPos->GetNode() );
|
|
if( pPos->GetNode() < *rNodes.GetEndOfContent().StartOfSectionNode() )
|
|
{
|
|
assert(nLevel > 0);
|
|
nLevel--;
|
|
}
|
|
do { SwNodes::GoStartOfSection( &pPos->nNode ); } while( nLevel-- );
|
|
|
|
// already on a ContentNode
|
|
pPos->AssignStartIndex(*pPos->GetNode().GetContentNode());
|
|
}
|
|
|
|
void GoStartOfSection( SwPosition * pPos )
|
|
{
|
|
// jump to section's beginning
|
|
SwNodes::GoStartOfSection( &pPos->nNode );
|
|
pPos->nContent.Assign(pPos->GetNode().GetContentNode(), 0);
|
|
}
|
|
|
|
/// go to the end of the current base section
|
|
void GoEndSection( SwPosition * pPos )
|
|
{
|
|
// jump to section's beginning/end
|
|
SwNodes& rNodes = pPos->GetNodes();
|
|
int nLevel = SwNodes::GetSectionLevel( pPos->GetNode() );
|
|
if( pPos->GetNode() < *rNodes.GetEndOfContent().StartOfSectionNode() )
|
|
{
|
|
assert(nLevel > 0);
|
|
nLevel--;
|
|
}
|
|
do { SwNodes::GoEndOfSection( &pPos->nNode ); } while( nLevel-- );
|
|
|
|
// now on an EndNode, thus to the previous ContentNode
|
|
if( SwContentNode* pCNd = GoPreviousNds( &pPos->nNode, true ) )
|
|
pPos->AssignEndIndex(*pCNd);
|
|
}
|
|
|
|
void GoEndOfSection( SwPosition * pPos )
|
|
{
|
|
SwNodes::GoEndOfSection( &pPos->nNode );
|
|
SwContentNode* pCNd = pPos->nNode.GetNode().GetContentNode();
|
|
pPos->nContent.Assign(pCNd, pCNd ? pCNd->Len() : 0);
|
|
}
|
|
|
|
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.fnPos)( rPam.GetPoint(), true );
|
|
if( pNd )
|
|
rPam.GetPoint()->SetContent(
|
|
::GetSttOrEnd( &fnMove == &fnMoveForward, *pNd ) );
|
|
return pNd;
|
|
}
|
|
|
|
bool GoInContent( SwPaM & rPam, SwMoveFnCollection const & fnMove )
|
|
{
|
|
if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
|
|
&rPam.GetPoint()->nContent, SwCursorSkipMode::Chars ))
|
|
return true;
|
|
return GoInNode( rPam, fnMove );
|
|
}
|
|
|
|
bool GoInContentCells( SwPaM & rPam, SwMoveFnCollection const & fnMove )
|
|
{
|
|
if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
|
|
&rPam.GetPoint()->nContent, SwCursorSkipMode::Cells ))
|
|
return true;
|
|
return GoInNode( rPam, fnMove );
|
|
}
|
|
|
|
bool GoInContentSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
|
|
{
|
|
if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
|
|
&rPam.GetPoint()->nContent, SwCursorSkipMode::Chars | SwCursorSkipMode::Hidden ) )
|
|
return true;
|
|
return GoInNode( rPam, fnMove );
|
|
}
|
|
|
|
bool GoInContentCellsSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
|
|
{
|
|
if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
|
|
&rPam.GetPoint()->nContent, SwCursorSkipMode::Cells | SwCursorSkipMode::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.GetNode().GetContentNode();
|
|
rPos.SetContent( ::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ) );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GoCurrPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
|
|
{
|
|
SwPosition& rPos = *rPam.GetPoint();
|
|
SwContentNode * pNd = rPos.GetNode().GetContentNode();
|
|
if( pNd )
|
|
{
|
|
const sal_Int32 nOld = rPos.GetContentIndex();
|
|
const sal_Int32 nNew = &aPosPara == &fnMoveForward ? 0 : pNd->Len();
|
|
// if already at beginning/end then to the next/previous
|
|
if( nOld != nNew )
|
|
{
|
|
rPos.SetContent( nNew );
|
|
return true;
|
|
}
|
|
}
|
|
// move node to next/previous ContentNode
|
|
if( ( &aPosPara==&fnParaStart && nullptr != ( pNd =
|
|
GoPreviousPos( &rPos, true ))) ||
|
|
( &aPosPara==&fnParaEnd && nullptr != ( pNd =
|
|
GoNextPos( &rPos, true ))) )
|
|
{
|
|
rPos.SetContent( ::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.GetNode().GetContentNode();
|
|
rPos.SetContent( ::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 );
|
|
SwContentNode *pNd;
|
|
if( nullptr == ( pNd = rPos.GetNode().GetContentNode()) &&
|
|
nullptr == ( pNd = (*fnMove.fnPos)( &rPos, true )) )
|
|
{
|
|
rPos = aSavePos; // do not change cursor
|
|
return false;
|
|
}
|
|
|
|
rPos.SetContent( ::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)
|
|
{
|
|
if (!bIsStartNode)
|
|
{
|
|
aResult.append(CH_TXTATR_NEWLINE); // use newline for para break
|
|
}
|
|
const OUString& aTmpStr = pTextNode->GetText();
|
|
|
|
if (!aTmpStr.isEmpty() && (bIsStartNode || bIsEndNode))
|
|
{
|
|
// Handle corner cases of start/end node(s)
|
|
const sal_Int32 nStart = bIsStartNode
|
|
? Start()->GetContentIndex()
|
|
: 0;
|
|
const sal_Int32 nEnd = bIsEndNode
|
|
? End()->GetContentIndex()
|
|
: aTmpStr.getLength();
|
|
|
|
aResult.append(aTmpStr.subView(nStart, nEnd-nStart));
|
|
}
|
|
else
|
|
{
|
|
aResult.append(aTmpStr);
|
|
}
|
|
}
|
|
|
|
if (bIsEndNode)
|
|
{
|
|
break;
|
|
}
|
|
|
|
++aNodeIndex;
|
|
bIsStartNode = false;
|
|
}
|
|
|
|
return aResult.makeStringAndClear();
|
|
}
|
|
|
|
void SwPaM::InvalidatePaM()
|
|
{
|
|
for (SwNodeIndex index(Start()->GetNode()); index <= End()->GetNode(); ++index)
|
|
{
|
|
if (SwTextNode *const pTextNode = index.GetNode().GetTextNode())
|
|
{
|
|
// pretend that the PaM marks changed formatting to reformat...
|
|
sal_Int32 const nStart(
|
|
index == Start()->nNode ? Start()->GetContentIndex() : 0);
|
|
// this should work even for length of 0
|
|
SwUpdateAttr const aHint(
|
|
nStart,
|
|
index == End()->nNode
|
|
? End()->GetContentIndex() - nStart
|
|
: pTextNode->Len() - nStart,
|
|
0);
|
|
pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
|
|
}
|
|
// other node types not invalidated
|
|
}
|
|
}
|
|
|
|
void SwPaM::dumpAsXml(xmlTextWriterPtr pWriter) const
|
|
{
|
|
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPaM"));
|
|
|
|
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
|
|
GetPoint()->dumpAsXml(pWriter);
|
|
(void)xmlTextWriterEndElement(pWriter);
|
|
|
|
if (HasMark())
|
|
{
|
|
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("mark"));
|
|
GetMark()->dumpAsXml(pWriter);
|
|
(void)xmlTextWriterEndElement(pWriter);
|
|
}
|
|
|
|
(void)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: */
|