/* -*- 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 .
*/
/* * Some local helper functions for the attribute set handle of a content node. * Since the attribute set of a content node may not be modified directly, * we always have to create a new SwAttrSet, do the modifications, and get * a new handle from the style access
*/
// for a correct broadcast, we need to do a SetModifyAtAttr with the items // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle if( rNode.GetModifyAtAttr() )
aNewSet.SetModifyAtAttr( &rNode );
// for a correct broadcast, we need to do a SetModifyAtAttr with the items // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle if( rNode.GetModifyAtAttr() )
aNewSet.SetModifyAtAttr( &rNode );
/** Returns the section level at the position given by aIndex. * * We use the following logic: * S = Start, E = End, C = ContentNode * Level 0 = E * 1 = S E * 2 = SC * * All EndNodes of the BaseSection have level 0 * All StartNodes of the BaseSection have level 1
*/
sal_uInt16 SwNode::GetSectionLevel() const
{ // EndNode of a BaseSection? They are always 0! if( IsEndNode() && SwNodeOffset(0) == m_pStartOfSection->StartOfSectionIndex() ) return 0;
/** Inserts a node into the rNodes array at the rWhere position * * @param rNodes the variable array in that the node will be inserted * @param nPos position within the array where the node will be inserted * @param nNdType the type of node to insert
*/
SwNode::SwNode( SwNodes& rNodes, SwNodeOffset nPos, const SwNodeType nNdType )
: m_nNodeType( nNdType )
, m_nAFormatNumLvl( 0 )
, m_bIgnoreDontExpand( false)
, m_eMerge(Merge::None) #ifdef DBG_UTIL
, m_nSerial( s_nSerial++) #endif
, m_pStartOfSection( nullptr )
{ if( !nPos ) return;
SwNode::~SwNode()
{
assert(m_aAnchoredFlys.empty() || GetDoc().IsInDtor()); // must all be deleted
InvalidateInSwCache();
assert(!IsInCache());
}
/// Find the TableNode in which it is located. /// If we're not in a table: return 0
SwTableNode* SwNode::FindTableNode()
{ if( IsTableNode() ) return GetTableNode();
SwStartNode* pTmp = m_pStartOfSection; while( !pTmp->IsTableNode() && pTmp->GetIndex() )
pTmp = pTmp->m_pStartOfSection; return pTmp->GetTableNode();
}
/// Is the node located in the visible area of the Shell? bool SwNode::IsInVisibleArea( SwViewShell const * pSh ) const
{ bool bRet = false; const SwContentNode* pNd;
/// Find the PageDesc that is used to format this node. If the Layout is available, /// we search through that. Else we can only do it the hard way by searching onwards through the nodes. const SwPageDesc* SwNode::FindPageDesc( SwNodeOffset* pPgDescNdIdx ) const
{ if ( !GetNodes().IsDocNodes() )
{ return nullptr;
}
pFlyNd = pAnchor->GetAnchorNode()->FindFlyStartNode(); break;
}
} if( n >= rFormats.size() )
{
OSL_ENSURE( false, "FlySection, but no Format found" ); return nullptr;
}
}
}
} // pNd should now contain the correct Anchor or it's still this
}
if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
{ if( pNd->GetIndex() > GetNodes().GetEndOfAutotext().GetIndex() )
{
pPgDesc = &rDoc.GetPageDesc( 0 );
pNd = nullptr;
} else
{ // Find the Body text node if( nullptr != ( pSttNd = pNd->FindHeaderStartNode() ) ||
nullptr != ( pSttNd = pNd->FindFooterStartNode() ))
{ // Then find this StartNode in the PageDescs
sal_uInt16 nId;
UseOnPage eAskUse; if( SwHeaderStartNode == pSttNd->GetStartNodeType())
{
nId = RES_HEADER;
eAskUse = UseOnPage::HeaderShare;
} else
{
nId = RES_FOOTER;
eAskUse = UseOnPage::FooterShare;
}
if( !pPgDesc )
pPgDesc = &rDoc.GetPageDesc( 0 );
pNd = nullptr;
} elseif( nullptr != ( pSttNd = pNd->FindFootnoteStartNode() ))
{ // the Anchor can only be in the Body text const SwTextFootnote* pTextFootnote; const SwFootnoteIdxs& rFootnoteArr = rDoc.GetFootnoteIdxs(); for( size_t n = 0; n < rFootnoteArr.size(); ++n ) if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() && static_cast<SwNode const *>(pSttNd) ==
&pTextFootnote->GetStartNode()->GetNode() )
{
pNd = &pTextFootnote->GetTextNode(); break;
}
} else
{ // Can only be a page-bound Fly (or something newer). // we can only return the standard here
OSL_ENSURE( pNd->FindFlyStartNode(), "Where is this Node?" );
/// If the node is located in a Fly, we return it formatted accordingly
SwFrameFormat* SwNode::GetFlyFormat() const
{
SwFrameFormat* pRet = nullptr; const SwNode* pSttNd = FindFlyStartNode(); if( pSttNd )
{ if( IsContentNode() )
{
SwContentFrame* pFrame = SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<const SwContentNode*>(this)).First(); if( pFrame && pFrame->FindFlyFrame())
pRet = pFrame->FindFlyFrame()->GetFormat();
} if( !pRet )
{ // The hard way through the Doc is our last way out const sw::SpzFrameFormats& rSpzs = *GetDoc().GetSpzFrameFormats(); for(sw::SpzFrameFormat* pSpz: rSpzs)
{ // Only Writer fly frames can contain Writer nodes. if (pSpz->Which() != RES_FLYFRMFMT) continue; const SwFormatContent& rContent = pSpz->GetContent(); if( rContent.GetContentIdx() &&
&rContent.GetContentIdx()->GetNode() == pSttNd )
{
pRet = pSpz; break;
}
}
}
} return pRet;
}
if( bCheckFirst )
{ // The first OutlineNode comes after the one asking. // Test if both are on the same page. // If not it's invalid. for (nPos = 0; nPos < rONds.size(); ++nPos)
{
pRet = rONds[nPos]->GetTextNode(); if (!pLayout || sw::IsParaPropsNode(*pLayout, *pRet))
{ break;
}
} if (nPos == rONds.size())
{ return nullptr;
}
// (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
}
/** Insert a node into the array * * The StartOfSection pointer is set to the given node. * * The EndOfSection pointer of the corresponding start node is set to this node. * * @param rWhere position where the node should be inserted * @param rSttNd the start note of the section
*/
SwContentNode::~SwContentNode()
{ // The base class SwClient of SwFrame excludes itself from the dependency list! // Thus, we need to delete all Frames in the dependency list. if (!IsTextNode()) // see ~SwTextNode
{
DelFrames(nullptr);
}
void SwContentNode::SwClientNotify( const SwModify&, const SfxHint& rHint)
{ if (rHint.GetId() == SfxHintId::SwFormatChange)
{ auto pChangeHint = static_cast<const SwFormatChangeHint*>(&rHint);
InvalidateInSwCache();
// If the Format parent was switched, register the Attrset at the new one // Skip own Modify! bool bSetParent = false;
SwFormatColl* pFormatColl = nullptr; if(GetpSwAttrSet()
&& pChangeHint->m_pNewFormat == GetRegisteredIn())
{
pFormatColl = GetFormatColl();
bSetParent = true;
}
if(bSetParent && GetpSwAttrSet())
AttrSetHandleHelper::SetParent(mpAttrSet, *this, pFormatColl, pFormatColl);
CallSwClientNotify(rHint);
} elseif (rHint.GetId() == SfxHintId::SwAttrSetChange)
{ auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
InvalidateInSwCache(); if (GetNodes().IsDocNodes()
&& IsTextNode()
&& pChangeHint->m_pOld
&& SfxItemState::SET == pChangeHint->m_pOld->GetChgSet()->GetItemState(RES_CHRATR_HIDDEN, false)) static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
CallSwClientNotify(rHint);
} elseif (rHint.GetId() == SfxHintId::SwObjectDying)
{ auto pDyingHint = static_cast<const sw::ObjectDyingHint*>(&rHint);
InvalidateInSwCache(); const SwFormat* pFormat = static_cast<const SwFormat*>(pDyingHint->m_pDying); // Do not mangle pointers if it is the upper-most format! if(pFormat && GetRegisteredIn() == pFormat)
{ // As ~SwFormat calls CheckRegistrationFormat before // ~SwModify, which sends the RES_OBJECTDYING, we should never // reach this point.
assert(false);
}
CallSwClientNotify(rHint);
} elseif (rHint.GetId() == SfxHintId::SwLegacyModify)
{ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint); const sal_uInt16 nWhich = pLegacyHint->GetWhich();
InvalidateInSwCache(nWhich);
CallSwClientNotify(rHint);
} elseif (rHint.GetId() == SfxHintId::SwUpdateAttr)
{ auto pUpdateAttrHint = static_cast<const sw::UpdateAttrHint*>(&rHint);
// RES_UPDATE_ATTR _should_ always contain a SwUpdateAttr hint in old and new. // However, faking one with just a basic SfxPoolItem setting a WhichId has been observed. // This makes the crude "WhichId" type divert from the true type, which is bad. // Thus we are asserting here, but falling back to an proper // hint instead. so that we at least will not spread such poison further. #ifdef DBG_UTIL if (pUpdateAttrHint->m_pNew != pUpdateAttrHint->m_pOld)
{ auto pBT = sal::backtrace_get(20);
SAL_WARN("sw.core", "UpdateAttr not matching! " << sal::backtrace_to_string(pBT.get()));
} #endif
assert(pUpdateAttrHint->m_pNew == pUpdateAttrHint->m_pOld); const SwUpdateAttr aFallbackHint(0,0,0); const SwUpdateAttr& rUpdateAttr = pUpdateAttrHint->m_pNew ? *pUpdateAttrHint->m_pNew : aFallbackHint;
UpdateAttr(rUpdateAttr);
} elseif (rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
{ static_cast<const sw::AutoFormatUsedHint&>(rHint).CheckNode(this); return;
} elseif (rHint.GetId() == SfxHintId::SwModifyChanged)
{ auto pModifyChangedHint = static_cast<const sw::ModifyChangedHint*>(&rHint);
m_pCondColl = const_cast<SwFormatColl*>(static_cast<const SwFormatColl*>(pModifyChangedHint->m_pNew));
} elseif(rHint.GetId() == SfxHintId::SwCondCollCondChg)
{ auto pCondCollCondChgHint = static_cast<const sw::CondCollCondChg*>(&rHint);
ChkCondColl(&pCondCollCondChgHint->m_rColl);
}
}
// Set the Parent of out AutoAttributes to the new Collection if( GetpSwAttrSet() )
AttrSetHandleHelper::SetParent( mpAttrSet, *this, pNewColl, pNewColl );
/** * Creates all Views for the Doc for this Node. * The created ContentFrames are attached to the corresponding Layout.
*/ void SwContentNode::MakeFramesForAdjacentContentNode(SwContentNode& rNode)
{
OSL_ENSURE( &rNode != this, "No ContentNode or CopyNode and new Node identical." );
if( !HasWriterListeners() || &rNode == this ) // Do we actually have Frames? return;
SwFrame *pFrame;
SwLayoutFrame *pUpper; // Create Frames for Nodes which come after the Table?
OSL_ENSURE( FindTableNode() == rNode.FindTableNode(), "Table confusion" );
while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, rNode )) )
{ if (pUpper->getRootFrame()->HasMergedParas()
&& !rNode.IsCreateFrameWhenHidingRedlines())
{ continue;
}
SwFrame *pNew = rNode.MakeFrame( pUpper );
pNew->Paste( pUpper, pFrame ); // #i27138# // notify accessibility paragraphs objects about changed // CONTENT_FLOWS_FROM/_TO relation. // Relation CONTENT_FLOWS_FROM for next paragraph will change // and relation CONTENT_FLOWS_TO for previous paragraph will change. #if !ENABLE_WASM_STRIP_ACCESSIBILITY if ( pNew->IsTextFrame() )
{
SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() ); if ( pViewShell && pViewShell->GetLayout() &&
pViewShell->GetLayout()->IsAnyShellAccessible() )
{ auto pNext = pNew->FindNextCnt( true ); auto pPrev = pNew->FindPrevCnt();
pViewShell->InvalidateAccessibleParaFlowRelation(
pNext ? pNext->DynCastTextFrame() : nullptr,
pPrev ? pPrev->DynCastTextFrame() : nullptr );
}
} #endif
}
}
/** * Deletes all Views from the Doc for this Node. * The ContentFrames are removed from the corresponding Layout.
*/ void SwContentNode::DelFrames(SwRootFrame const*const pLayout)
{ if( !HasWriterListeners() ) return;
SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*this); for( SwContentFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
{ if (pLayout && pLayout != pFrame->getRootFrame())
{ continue; // skip it
} if (pFrame->IsTextFrame())
{ if (sw::MergedPara * pMerged = static_cast<SwTextFrame *>(pFrame)->GetMergedPara())
{ if (this != pMerged->pFirstNode)
{ // SwNodes::RemoveNode iterates *backwards* - so // ensure there are no more extents pointing to this // node as SwFrame::InvalidatePage() will access them. // Note: cannot send via SwClientNotify from dtor // because that would access deleted wrong-lists
sw::UpdateMergedParaForDelete(*pMerged,
pFrame->getRootFrame()->GetParagraphBreakMode(), static_cast<SwTextFrame *>(pFrame)->GetScriptInfo(), true, *static_cast<SwTextNode*>(this), 0, Len()); if (this == pMerged->pParaPropsNode)
{ // otherwise pointer should have been updated to a different node
assert(this == pMerged->pLastNode);
assert(pMerged->extents.empty()); for (SwNodeOffset i = pMerged->pLastNode->GetIndex() - 1;;
--i)
{
assert(pMerged->pFirstNode->GetIndex() <= i);
SwNode *const pNode(GetNodes()[i]); if (pNode->IsTextNode()
&& pNode->GetRedlineMergeFlag() != Merge::Hidden)
{
pMerged->pParaPropsNode = pNode->GetTextNode(); break;
} elseif (pMerged->pFirstNode->GetIndex() == i)
{ // this can only happen when called from CheckParaRedlineMerge() // and the pMerged will be deleted anyway
pMerged->pParaPropsNode = pMerged->pFirstNode; break;
}
}
assert(pMerged->listener.IsListeningTo(pMerged->pParaPropsNode));
}
assert(GetIndex() <= pMerged->pLastNode->GetIndex()); if (this == pMerged->pLastNode)
{ // tdf#130680 find the previous node that is a // listener of pMerged; see CheckParaRedlineMerge() for (SwNodeOffset i = GetIndex() - 1; this == pMerged->pLastNode; --i)
{
SwNode *const pNode = GetNodes()[i]; if (pNode->IsTextNode())
{
pMerged->pLastNode = pNode->GetTextNode();
} elseif (SwEndNode const*const pEnd = pNode->GetEndNode())
{
SwStartNode const*const pStart(pEnd->StartOfSectionNode());
i = pStart->GetIndex(); // skip table or section
}
}
assert(pMerged->pFirstNode->GetIndex() <= pMerged->pLastNode->GetIndex());
assert(pMerged->listener.IsListeningTo(pMerged->pLastNode));
} // avoid re-parenting mess (ModifyChangedHint)
pMerged->listener.EndListening(this); continue; // don't delete
}
} // #i27138# // notify accessibility paragraphs objects about changed // CONTENT_FLOWS_FROM/_TO relation. // Relation CONTENT_FLOWS_FROM for current next paragraph will change // and relation CONTENT_FLOWS_TO for current previous paragraph will change. #if !ENABLE_WASM_STRIP_ACCESSIBILITY
SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); if ( pViewShell && pViewShell->GetLayout() &&
pViewShell->GetLayout()->IsAnyShellAccessible() )
{ auto pNext = pFrame->FindNextCnt( true ); auto pPrev = pFrame->FindPrevCnt();
pViewShell->InvalidateAccessibleParaFlowRelation(
pNext ? pNext->DynCastTextFrame() : nullptr,
pPrev ? pPrev->DynCastTextFrame() : nullptr );
} #endif
}
if( pFrame->IsFollow() )
{
SwContentFrame* pMaster = pFrame->FindMaster();
pMaster->SetFollow( pFrame->GetFollow() );
}
pFrame->SetFollow( nullptr );//So it doesn't get funny ideas. //Otherwise it could be possible that a follow //gets destroyed before its master. Following //the now invalid pointer will then lead to an //illegal memory access. The chain can be //crushed here because we'll destroy all of it //anyway.
/// Get info from Modify bool SwContentNode::GetInfo( SwFindNearestNode& rInfo ) const
{ if( GetAttr( RES_PAGEDESC ).GetPageDesc() )
rInfo.CheckNode( *this ); returntrue;
}
/// @param rAttr the attribute to set bool SwContentNode::SetAttr(const SfxPoolItem& rAttr )
{ if( !GetpSwAttrSet() ) // Have the Nodes created by the corresponding AttrSets
NewAttrSet( GetDoc().GetAttrPool() );
OSL_ENSURE( GetpSwAttrSet(), "Why did't we create an AttrSet?");
// If there already is an attribute set (usually containing a numbering // item), we have to merge the attribute of the new set into the old set: bool bSetParent = true; if ( GetpSwAttrSet() )
{
bSetParent = false;
AttrSetHandleHelper::Put( mpAttrSet, *this, *pFnd->GetStyleHandle() );
} else
{
std::shared_ptr<SfxItemSet> pItemSet = pFnd->GetStyleHandle();
mpAttrSet = std::dynamic_pointer_cast<SwAttrSet>(pItemSet);
assert(bool(pItemSet) == bool(mpAttrSet) && "types do not match");
}
if ( bSetParent )
{ // If the content node has a conditional style, we have to set the // string item containing the correct conditional style name (the // style name property has already been set during the import!) // In case we do not have a conditional style, we make use of the // fact that nobody else uses the attribute set behind the handle. // FME 2007-07-10 #i78124# If autostyle does not have a parent, // the string is empty. const SfxStringItem* pNameItem = nullptr; if ( nullptr != GetCondFormatColl() ||
!(pNameItem = mpAttrSet->GetItemIfSet( RES_FRMATR_STYLE_NAME, false )) ||
pNameItem->GetValue().isEmpty() )
AttrSetHandleHelper::SetParent( mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl() ); else const_cast<SwAttrSet*>(mpAttrSet.get())->SetParent( &GetFormatColl()->GetAttrSet() );
}
returntrue;
}
if( !GetpSwAttrSet() ) // Have the AttrsSets created by the corresponding Nodes
NewAttrSet( GetDoc().GetAttrPool() );
bool bRet = false; // If Modify is locked, do not send any Modifys if ( IsModifyLocked() ||
( !HasWriterListeners() &&
SfxItemState::SET != rSet.GetItemState( RES_PARATR_NUMRULE, false ) ) )
{ // Some special treatment for Attributes
bRet = AttrSetHandleHelper::Put( mpAttrSet, *this, rSet );
} else
{
SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rSet, &aOld, &aNew ); if( bRet )
sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
} return bRet;
}
// With nWhich it takes the Hint from the Delta array bool SwContentNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
{ if( !GetpSwAttrSet() ) returnfalse;
InvalidateInSwCache();
// If Modify is locked, do not send out any Modifys if( IsModifyLocked() )
{
sal_uInt16 nDel = 0; if ( !nWhich2 || nWhich2 < nWhich1 )
{
nDel = ClearItemsFromAttrSet( { nWhich1 } );
} else
nDel = AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, nullptr, nullptr );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.