summaryrefslogtreecommitdiffstats
path: root/sw/source/core/crsr/pam.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/crsr/pam.cxx')
-rw-r--r--sw/source/core/crsr/pam.cxx1210
1 files changed, 1210 insertions, 0 deletions
diff --git a/sw/source/core/crsr/pam.cxx b/sw/source/core/crsr/pam.cxx
new file mode 100644
index 000000000..78bdbe365
--- /dev/null
+++ b/sw/source/core/crsr/pam.cxx
@@ -0,0 +1,1210 @@
+/* -*- 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 <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 <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 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
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPosition"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nNode"), BAD_CAST(OString::number(sal_Int32(nNode.GetIndex())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nContent"), BAD_CAST(OString::number(nContent.GetIndex()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+std::ostream &operator <<(std::ostream& s, const SwPosition& position)
+{
+ return s << "SwPosition (node " << position.nNode.GetIndex() << ", offset " << position.nContent.GetIndex() << ")";
+}
+
+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 SwNodeIndex& rStt,
+ const SwNodeIndex& 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, 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 && SwNodeOffset(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 && SwNodeOffset(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,
+ 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.nNode.GetNode().GetContentNode(), 0 );
+ m_Bound2.nContent.Assign( m_Bound2.nNode.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.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 )
+{
+ 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;
+}
+
+#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;
+}
+
+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.
+
+ @return Newly created range, in Ring with parameter pOrigRg.
+*/
+std::unique_ptr<SwPaM> MakeRegion(SwMoveFnCollection const & fnMove,
+ const SwPaM & rOrigRg)
+{
+ std::unique_ptr<SwPaM> pPam;
+ {
+ pPam.reset(new SwPaM(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( (pPam->GetMark()->*fnMove.fnCmpOp)( *pPam->GetPoint() ) )
+ pPam->Exchange();
+ }
+ return pPam;
+}
+
+} // 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->nNode.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 ) const
+{
+ bool bRet = false;
+
+ const SwContentNode* pNd = GetPoint()->nNode.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()->nNode.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()->nNode.GetIndex(),
+ nEndIdx = GetPoint()->nNode.GetIndex();
+ if( nEndIdx <= nSttIdx )
+ {
+ SwNodeOffset 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 + 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::IFieldmark* pA = GetPoint() ? pMarksAccess->getFieldmarkFor( *GetPoint( ) ) : nullptr;
+ sw::mark::IFieldmark* pB = GetMark() ? pMarksAccess->getFieldmarkFor( *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))
+ {
+ return true;
+ }
+ }
+ if (!bRet &&
+ rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FIELDS))
+ {
+ SwPosition const& rStart(*Start());
+ SwPosition const& rEnd(*End());
+ for (SwNodeIndex n = rStart.nNode; n <= rEnd.nNode; ++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.nNode && pHint->GetStart() < rStart.nContent.GetIndex())
+ {
+ continue; // before selection
+ }
+ if (n == rEnd.nNode && rEnd.nContent.GetIndex() <= 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->nNode.GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ sal_Int32 nIndex = pStart->nContent.GetIndex();
+ SwTextAttr* pAttr
+ = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::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())
+ {
+ bRet = pContentControl->GetCheckbox() || pContentControl->GetPicture();
+ }
+ }
+ }
+ }
+
+ 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.GetContentNode();
+ if( pNd )
+ {
+ SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
+ if(
+ (
+ nullptr == pFrame ||
+ ( !bInReadOnly && pFrame->IsProtected() ) ||
+ (pFrame->IsTextFrame() && static_cast<SwTextFrame const*>(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 )
+ {
+ if (i_pLayout && aPos.nNode.GetNode().IsTextNode())
+ {
+ auto const fal(sw::GetFirstAndLastNode(*pLayout, aPos.nNode));
+ aPos.nNode = bSrchForward ? *fal.second : *fal.first;
+ }
+
+ 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
+ SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
+ if (nullptr == pFrame ||
+ ( !bInReadOnly && pFrame->IsProtected() ) ||
+ ( pFrame->IsTextFrame() &&
+ static_cast<SwTextFrame const*>(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 an 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)
+ {
+ if (!bIsStartNode)
+ {
+ aResult.append(CH_TXTATR_NEWLINE); // use newline for para break
+ }
+ 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.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()->nNode; index <= End()->nNode; ++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()->nContent.GetIndex() : 0);
+ // this should work even for length of 0
+ SwUpdateAttr const aHint(
+ nStart,
+ index == End()->nNode
+ ? End()->nContent.GetIndex() - 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: */