/* -*- 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: */
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'Assign' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.