/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ndsect.hxx" using namespace ::com::sun::star; namespace { class SwIntrnlSectRefLink : public SwBaseLink { SwSectionFormat& rSectFormat; public: SwIntrnlSectRefLink( SwSectionFormat& rFormat, SfxLinkUpdateMode nUpdateType ) : SwBaseLink( nUpdateType, SotClipboardFormatId::RTF ), rSectFormat( rFormat ) {} virtual void Closed() override; virtual ::sfx2::SvBaseLink::UpdateResult DataChanged( const OUString& rMimeType, const css::uno::Any & rValue ) override; virtual const SwNode* GetAnchor() const override; virtual bool IsInRange( sal_uLong nSttNd, sal_uLong nEndNd ) const override; SwSectionNode* GetSectNode() { const SwNode* pSectNd( GetAnchor() ); return const_cast( dynamic_cast( pSectNd ) ); } }; } SwSectionData::SwSectionData(SectionType const eType, OUString const& rName) : m_eType(eType) , m_sSectionName(rName) , m_bHiddenFlag(false) , m_bProtectFlag(false) , m_bEditInReadonlyFlag(false) // edit in readonly sections , m_bHidden(false) , m_bCondHiddenFlag(true) , m_bConnectFlag(true) { } // this must have the same semantics as operator=() SwSectionData::SwSectionData(SwSection const& rSection) : m_eType(rSection.GetType()) , m_sSectionName(rSection.GetSectionName()) , m_sCondition(rSection.GetCondition()) , m_sLinkFileName(rSection.GetLinkFileName()) , m_sLinkFilePassword(rSection.GetLinkFilePassword()) , m_Password(rSection.GetPassword()) , m_bHiddenFlag(rSection.IsHiddenFlag()) , m_bProtectFlag(rSection.IsProtect()) // edit in readonly sections , m_bEditInReadonlyFlag(rSection.IsEditInReadonly()) , m_bHidden(rSection.IsHidden()) , m_bCondHiddenFlag(true) , m_bConnectFlag(rSection.IsConnectFlag()) { } // this must have the same semantics as operator=() SwSectionData::SwSectionData(SwSectionData const& rOther) : m_eType(rOther.m_eType) , m_sSectionName(rOther.m_sSectionName) , m_sCondition(rOther.m_sCondition) , m_sLinkFileName(rOther.m_sLinkFileName) , m_sLinkFilePassword(rOther.m_sLinkFilePassword) , m_Password(rOther.m_Password) , m_bHiddenFlag(rOther.m_bHiddenFlag) , m_bProtectFlag(rOther.m_bProtectFlag) // edit in readonly sections , m_bEditInReadonlyFlag(rOther.m_bEditInReadonlyFlag) , m_bHidden(rOther.m_bHidden) , m_bCondHiddenFlag(true) , m_bConnectFlag(rOther.m_bConnectFlag) { } // the semantics here are weird for reasons of backward compatibility SwSectionData & SwSectionData::operator= (SwSectionData const& rOther) { m_eType = rOther.m_eType; m_sSectionName = rOther.m_sSectionName; m_sCondition = rOther.m_sCondition; m_sLinkFileName = rOther.m_sLinkFileName; m_sLinkFilePassword = rOther.m_sLinkFilePassword; m_bConnectFlag = rOther.m_bConnectFlag; m_Password = rOther.m_Password; m_bEditInReadonlyFlag = rOther.m_bEditInReadonlyFlag; m_bProtectFlag = rOther.m_bProtectFlag; m_bHidden = rOther.m_bHidden; // FIXME: old code did not assign m_bHiddenFlag ? // FIXME: why should m_bCondHiddenFlag always default to true? m_bCondHiddenFlag = true; return *this; } // the semantics here are weird for reasons of backward compatibility bool SwSectionData::operator==(SwSectionData const& rOther) const { return (m_eType == rOther.m_eType) && (m_sSectionName == rOther.m_sSectionName) && (m_sCondition == rOther.m_sCondition) && (m_bHidden == rOther.m_bHidden) && (m_bProtectFlag == rOther.m_bProtectFlag) && (m_bEditInReadonlyFlag == rOther.m_bEditInReadonlyFlag) && (m_sLinkFileName == rOther.m_sLinkFileName) && (m_sLinkFilePassword == rOther.m_sLinkFilePassword) && (m_Password == rOther.m_Password); // FIXME: old code ignored m_bCondHiddenFlag m_bHiddenFlag m_bConnectFlag } OUString SwSectionData::CollapseWhiteSpaces(const OUString& sName) { const sal_Int32 nLen = sName.getLength(); const sal_Unicode cRef = ' '; OUStringBuffer aBuf(nLen+1); for (sal_Int32 i = 0; iIsHiddenFlag() ) { SetHidden(); } m_Data.SetProtectFlag( pParentSect->IsProtectFlag() ); // edit in readonly sections m_Data.SetEditInReadonlyFlag( pParentSect->IsEditInReadonlyFlag() ); } if (!m_Data.IsProtectFlag()) { m_Data.SetProtectFlag( rFormat.GetProtect().IsContentProtected() ); } if (!m_Data.IsEditInReadonlyFlag()) // edit in readonly sections { m_Data.SetEditInReadonlyFlag( rFormat.GetEditInReadonly().GetValue() ); } } SwSection::~SwSection() { SwSectionFormat* pFormat = GetFormat(); if( !pFormat ) return; SwDoc* pDoc = pFormat->GetDoc(); if( pDoc->IsInDtor() ) { // We reattach our Format to the default FrameFormat // to not get any dependencies if( pFormat->DerivedFrom() != pDoc->GetDfltFrameFormat() ) pFormat->RegisterToFormat( *pDoc->GetDfltFrameFormat() ); } else { pFormat->Remove( this ); // remove if (SectionType::Content != m_Data.GetType()) { pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() ); } if (m_RefObj.is()) { pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_RefObj.get() ); } // If the Section is the last Client in the Format we can delete it SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, pFormat ); pFormat->ModifyNotification( &aMsgHint, &aMsgHint ); if( !pFormat->HasWriterListeners() ) { // Do not add to the Undo. This should've happened earlier. ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); pDoc->DelSectionFormat( pFormat ); } } if (m_RefObj.is()) { m_RefObj->Closed(); } } void SwSection::SetSectionData(SwSectionData const& rData) { bool const bOldHidden( m_Data.IsHidden() ); m_Data = rData; // The next two may actually overwrite the m_Data.m_bProtect or EditInReadonly Flag // in Modify, which should result in same flag value as the old code! SetProtect(m_Data.IsProtectFlag()); SetEditInReadonly(m_Data.IsEditInReadonlyFlag()); if (bOldHidden != m_Data.IsHidden()) // check if changed... { ImplSetHiddenFlag(m_Data.IsHidden(), m_Data.IsCondHidden()); } } bool SwSection::DataEquals(SwSectionData const& rCmp) const { // note that the old code compared the flags of the parameter with the // format attributes of this; the following mess should do the same... (void) GetLinkFileName(); // updates m_sLinkFileName bool const bProtect(m_Data.IsProtectFlag()); bool const bEditInReadonly(m_Data.IsEditInReadonlyFlag()); m_Data.SetProtectFlag(IsProtect()); m_Data.SetEditInReadonlyFlag(IsEditInReadonly()); bool const bResult( m_Data == rCmp ); m_Data.SetProtectFlag(bProtect); m_Data.SetEditInReadonlyFlag(bEditInReadonly); return bResult; } void SwSection::ImplSetHiddenFlag(bool const bTmpHidden, bool const bCondition) { SwSectionFormat* pFormat = GetFormat(); OSL_ENSURE(pFormat, "ImplSetHiddenFlag: no format?"); if( pFormat ) { const bool bHide = bTmpHidden && bCondition; if (bHide) // should be hidden { if (!m_Data.IsHiddenFlag()) // is not hidden { // Is the Parent hidden? // This should be shown by the bHiddenFlag. // Tell all Children that they are hidden SwMsgPoolItem aMsgItem( RES_SECTION_HIDDEN ); pFormat->ModifyNotification( &aMsgItem, &aMsgItem ); // Delete all Frames pFormat->DelFrames(); } } else if (m_Data.IsHiddenFlag()) // show Nodes again { // Show all Frames (Child Sections are accounted for by MakeFrames) // Only if the Parent Section is not restricting us! SwSection* pParentSect = pFormat->GetParentSection(); if( !pParentSect || !pParentSect->IsHiddenFlag() ) { // Tell all Children that the Parent is not hidden anymore SwMsgPoolItem aMsgItem( RES_SECTION_NOT_HIDDEN ); pFormat->ModifyNotification( &aMsgItem, &aMsgItem ); pFormat->MakeFrames(); } } } } bool SwSection::CalcHiddenFlag() const { const SwSection* pSect = this; do { if( pSect->IsHidden() && pSect->IsCondHidden() ) return true; } while( nullptr != ( pSect = pSect->GetParent()) ); return false; } bool SwSection::IsProtect() const { SwSectionFormat const *const pFormat( GetFormat() ); OSL_ENSURE(pFormat, "SwSection::IsProtect: no format?"); return pFormat ? pFormat->GetProtect().IsContentProtected() : IsProtectFlag(); } // edit in readonly sections bool SwSection::IsEditInReadonly() const { SwSectionFormat const *const pFormat( GetFormat() ); OSL_ENSURE(pFormat, "SwSection::IsEditInReadonly: no format?"); return pFormat ? pFormat->GetEditInReadonly().GetValue() : IsEditInReadonlyFlag(); } void SwSection::SetHidden(bool const bFlag) { if (!m_Data.IsHidden() == !bFlag) return; m_Data.SetHidden(bFlag); ImplSetHiddenFlag(bFlag, m_Data.IsCondHidden()); } void SwSection::SetProtect(bool const bFlag) { SwSectionFormat *const pFormat( GetFormat() ); OSL_ENSURE(pFormat, "SwSection::SetProtect: no format?"); if (pFormat) { SvxProtectItem aItem( RES_PROTECT ); aItem.SetContentProtect( bFlag ); pFormat->SetFormatAttr( aItem ); // note: this will call m_Data.SetProtectFlag via Modify! } else { m_Data.SetProtectFlag(bFlag); } } // edit in readonly sections void SwSection::SetEditInReadonly(bool const bFlag) { SwSectionFormat *const pFormat( GetFormat() ); OSL_ENSURE(pFormat, "SwSection::SetEditInReadonly: no format?"); if (pFormat) { SwFormatEditInReadonly aItem; aItem.SetValue( bFlag ); pFormat->SetFormatAttr( aItem ); // note: this will call m_Data.SetEditInReadonlyFlag via Modify! } else { m_Data.SetEditInReadonlyFlag(bFlag); } } void SwSection::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) { bool bUpdateFootnote = false; switch( pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ) { case RES_ATTRSET_CHG: if (pNew && pOld) { SfxItemSet* pNewSet = const_cast(static_cast(pNew))->GetChgSet(); SfxItemSet* pOldSet = const_cast(static_cast(pOld))->GetChgSet(); const SfxPoolItem* pItem; if( SfxItemState::SET == pNewSet->GetItemState( RES_PROTECT, false, &pItem ) ) { m_Data.SetProtectFlag( static_cast(pItem) ->IsContentProtected() ); pNewSet->ClearItem( RES_PROTECT ); pOldSet->ClearItem( RES_PROTECT ); } // --> edit in readonly sections if( SfxItemState::SET == pNewSet->GetItemState( RES_EDIT_IN_READONLY, false, &pItem ) ) { m_Data.SetEditInReadonlyFlag( static_cast(pItem)->GetValue()); pNewSet->ClearItem( RES_EDIT_IN_READONLY ); pOldSet->ClearItem( RES_EDIT_IN_READONLY ); } if( SfxItemState::SET == pNewSet->GetItemState( RES_FTN_AT_TXTEND, false, &pItem ) || SfxItemState::SET == pNewSet->GetItemState( RES_END_AT_TXTEND, false, &pItem )) { bUpdateFootnote = true; } if( !pNewSet->Count() ) return; } break; case RES_PROTECT: if( pNew ) { bool bNewFlag = static_cast(pNew)->IsContentProtected(); if( !bNewFlag ) { // Switching off: See if there is protection transferred // by the Parents const SwSection* pSect = this; do { if( pSect->IsProtect() ) { bNewFlag = true; break; } pSect = pSect->GetParent(); } while (pSect); } m_Data.SetProtectFlag( bNewFlag ); } return; // edit in readonly sections case RES_EDIT_IN_READONLY: if( pNew ) { const bool bNewFlag = static_cast(pNew)->GetValue(); m_Data.SetEditInReadonlyFlag( bNewFlag ); } return; case RES_SECTION_HIDDEN: m_Data.SetHiddenFlag(true); return; case RES_SECTION_NOT_HIDDEN: m_Data.SetHiddenFlag( m_Data.IsHidden() && m_Data.IsCondHidden() ); return; case RES_COL: // Is handled by the Layout, if appropriate break; case RES_FTN_AT_TXTEND: if( pNew && pOld ) { bUpdateFootnote = true; } break; case RES_END_AT_TXTEND: if( pNew && pOld ) { bUpdateFootnote = true; } break; default: CheckRegistration( pOld ); break; } if( bUpdateFootnote ) { SwSectionNode* pSectNd = GetFormat()->GetSectionNode(); if( pSectNd ) pSectNd->GetDoc()->GetFootnoteIdxs().UpdateFootnote(SwNodeIndex( *pSectNd )); } } void SwSection::SetRefObject( SwServerObject* pObj ) { m_RefObj = pObj; } void SwSection::SetCondHidden(bool const bFlag) { if (!m_Data.IsCondHidden() == !bFlag) return; m_Data.SetCondHidden(bFlag); ImplSetHiddenFlag(m_Data.IsHidden(), bFlag); } // Set/remove the linked FileName OUString const & SwSection::GetLinkFileName() const { if (m_RefLink.is()) { OUString sTmp; switch (m_Data.GetType()) { case SectionType::DdeLink: sTmp = m_RefLink->GetLinkSourceName(); break; case SectionType::FileLink: { OUString sRange; OUString sFilter; if (m_RefLink->GetLinkManager() && sfx2::LinkManager::GetDisplayNames( m_RefLink.get(), nullptr, &sTmp, &sRange, &sFilter )) { sTmp += OUStringChar(sfx2::cTokenSeparator) + sFilter + OUStringChar(sfx2::cTokenSeparator) + sRange; } else if( GetFormat() && !GetFormat()->GetSectionNode() ) { // If the Section is in the UndoNodesArray, the LinkManager // does not contain the Link, thus it cannot be queried for it. // Thus return the current Name. return m_Data.GetLinkFileName(); } } break; default: break; } m_Data.SetLinkFileName(sTmp); } return m_Data.GetLinkFileName(); } void SwSection::SetLinkFileName(const OUString& rNew) { if (m_RefLink.is()) { m_RefLink->SetLinkSourceName( rNew ); } m_Data.SetLinkFileName(rNew); } // If it was a Linked Section, we need to make all Child Links visible void SwSection::MakeChildLinksVisible( const SwSectionNode& rSectNd ) { const SwNode* pNd; const ::sfx2::SvBaseLinks& rLnks = rSectNd.GetDoc()->getIDocumentLinksAdministration().GetLinkManager().GetLinks(); for( auto n = rLnks.size(); n; ) { sfx2::SvBaseLink& rBLnk = *rLnks[--n]; if (!rBLnk.IsVisible() && dynamic_cast(&rBLnk) != nullptr && nullptr != (pNd = static_cast(rBLnk).GetAnchor())) { pNd = pNd->StartOfSectionNode(); // If it's a SectionNode const SwSectionNode* pParent; while( nullptr != ( pParent = pNd->FindSectionNode() ) && ( SectionType::Content == pParent->GetSection().GetType() || pNd == &rSectNd )) pNd = pParent->StartOfSectionNode(); // It's within a normal Section, so show again if( !pParent ) rBLnk.SetVisible(true); } } } const SwTOXBase* SwSection::GetTOXBase() const { const SwTOXBase* pRet = nullptr; if( SectionType::ToxContent == GetType() ) pRet = dynamic_cast(this); return pRet; } SwSectionFormat::SwSectionFormat( SwFrameFormat* pDrvdFrame, SwDoc *pDoc ) : SwFrameFormat( pDoc->GetAttrPool(), OUString(), pDrvdFrame ) { LockModify(); SetFormatAttr( *GetDfltAttr( RES_COL ) ); UnlockModify(); } SwSectionFormat::~SwSectionFormat() { if( !GetDoc()->IsInDtor() ) { SwSectionNode* pSectNd; const SwNodeIndex* pIdx = GetContent( false ).GetContentIdx(); if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() && nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() )) { SwSection& rSect = pSectNd->GetSection(); // If it was a linked Section, we need to make all Child Links // visible again if( rSect.IsConnected() ) SwSection::MakeChildLinksVisible( *pSectNd ); // Check whether we need to be visible, before deleting the Nodes if( rSect.IsHiddenFlag() ) { SwSection* pParentSect = rSect.GetParent(); if( !pParentSect || !pParentSect->IsHiddenFlag() ) { // Make Nodes visible again rSect.SetHidden(false); } } // mba: test iteration; objects are removed while iterating // use hint which allows to specify, if the content shall be saved or not CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( true ) ); // Raise the Section up SwNodeRange aRg( *pSectNd, 0, *pSectNd->EndOfSectionNode() ); GetDoc()->GetNodes().SectionUp( &aRg ); } LockModify(); ResetFormatAttr( RES_CNTNT ); UnlockModify(); } } SwSection * SwSectionFormat::GetSection() const { return SwIterator( *this ).First(); } // Do not destroy all Frames in aDepend (Frames are recognized with a dynamic_cast). void SwSectionFormat::DelFrames() { SwSectionNode* pSectNd; const SwNodeIndex* pIdx = GetContent(false).GetContentIdx(); if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() && nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() )) { // First delete the of the instance // mba: test iteration as objects are removed in iteration // use hint which allows to specify, if the content shall be saved or not CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( false ) ); // Then delete frames of the nested instances SwIterator aIter( *this ); SwSectionFormat *pLast = aIter.First(); while ( pLast ) { pLast->DelFrames(); pLast = aIter.Next(); } sal_uLong nEnd = pSectNd->EndOfSectionIndex(); sal_uLong nStart = pSectNd->GetIndex()+1; sw_DeleteFootnote( pSectNd, nStart, nEnd ); } if( pIdx ) { // Send Hint for PageDesc. Actually the Layout contained in the // Paste of the Frame itself would need to do this. But that leads // to subsequent errors, which we'd need to solve at run-time. SwNodeIndex aNextNd( *pIdx ); SwContentNode* pCNd = GetDoc()->GetNodes().GoNextSection( &aNextNd, true, false ); if( pCNd ) { const SfxPoolItem& rItem = pCNd->GetSwAttrSet().Get( RES_PAGEDESC ); pCNd->ModifyNotification( &rItem, &rItem ); } } } // Create the Views void SwSectionFormat::MakeFrames() { SwSectionNode* pSectNd; const SwNodeIndex* pIdx = GetContent(false).GetContentIdx(); if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() && nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() )) { SwNodeIndex aIdx( *pIdx ); pSectNd->MakeOwnFrames( &aIdx ); } } void SwSectionFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) { bool bClients = false; sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; switch( nWhich ) { case RES_ATTRSET_CHG: if (HasWriterListeners() && pOld && pNew) { SfxItemSet* pNewSet = const_cast(static_cast(pNew))->GetChgSet(); SfxItemSet* pOldSet = const_cast(static_cast(pOld))->GetChgSet(); const SfxPoolItem *pItem; if( SfxItemState::SET == pNewSet->GetItemState( RES_PROTECT, false, &pItem )) { ModifyBroadcast( pItem, pItem ); pNewSet->ClearItem( RES_PROTECT ); pOldSet->ClearItem( RES_PROTECT ); } // --> edit in readonly sections if( SfxItemState::SET == pNewSet->GetItemState( RES_EDIT_IN_READONLY, false, &pItem ) ) { ModifyBroadcast( pItem, pItem ); pNewSet->ClearItem( RES_EDIT_IN_READONLY ); pOldSet->ClearItem( RES_EDIT_IN_READONLY ); } if( SfxItemState::SET == pNewSet->GetItemState( RES_FTN_AT_TXTEND, false, &pItem )) { ModifyBroadcast( &pOldSet->Get( RES_FTN_AT_TXTEND ), pItem ); pNewSet->ClearItem( RES_FTN_AT_TXTEND ); pOldSet->ClearItem( RES_FTN_AT_TXTEND ); } if( SfxItemState::SET == pNewSet->GetItemState( RES_END_AT_TXTEND, false, &pItem )) { ModifyBroadcast( &pOldSet->Get( RES_END_AT_TXTEND ), pItem ); pNewSet->ClearItem( RES_END_AT_TXTEND ); pOldSet->ClearItem( RES_END_AT_TXTEND ); } if( !static_cast(pOld)->GetChgSet()->Count() ) return; } break; case RES_FTN_AT_TXTEND: case RES_END_AT_TXTEND : bClients = true; [[fallthrough]]; case RES_SECTION_HIDDEN: case RES_SECTION_NOT_HIDDEN: { SwSection* pSect = GetSection(); if( pSect && ( bClients || ( RES_SECTION_HIDDEN == nWhich ? !pSect->IsHiddenFlag() : pSect->IsHiddenFlag() ) ) ) { ModifyBroadcast( pOld, pNew ); } } return ; case RES_PROTECT: case RES_EDIT_IN_READONLY: // edit in readonly sections // Pass through these Messages until the End of the tree! if( HasWriterListeners() ) { ModifyBroadcast( pOld, pNew ); } return; // That's it! case RES_OBJECTDYING: if( !GetDoc()->IsInDtor() && pOld && static_cast(pOld)->pObject == static_cast(GetRegisteredIn()) ) { // My Parents will be destroyed, so get the Parent's Parent // and update SwFrameFormat::Modify( pOld, pNew ); // Rewire first! UpdateParent(); return; } break; case RES_FMT_CHG: if( !GetDoc()->IsInDtor() && static_cast(pNew)->pChangedFormat == static_cast(GetRegisteredIn()) && dynamic_cast(static_cast(pNew)->pChangedFormat) != nullptr ) { // My Parent will be changed, thus I need to update SwFrameFormat::Modify( pOld, pNew ); // Rewire first! UpdateParent(); return; } break; } SwFrameFormat::Modify( pOld, pNew ); if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) { // invalidate cached uno object SetXTextSection(uno::Reference(nullptr)); } } // Get info from the Format bool SwSectionFormat::GetInfo( SfxPoolItem& rInfo ) const { switch( rInfo.Which() ) { case RES_FINDNEARESTNODE: if( GetFormatAttr( RES_PAGEDESC ).GetPageDesc() ) { const SwSectionNode* pNd = GetSectionNode(); if( pNd ) static_cast(rInfo).CheckNode( *pNd ); } return true; case RES_CONTENT_VISIBLE: { SwFrame* pFrame = SwIterator(*this).First(); // if the current section has no own frame search for the children if(!pFrame) { SwIterator aFormatIter(*this); SwSectionFormat* pChild = aFormatIter.First(); while(pChild && !pFrame) { pFrame = SwIterator(*pChild).First(); pChild = aFormatIter.Next(); } } static_cast(rInfo).pObject = pFrame; } return false; } return SwModify::GetInfo( rInfo ); } static bool lcl_SectionCmpPos( const SwSection *pFirst, const SwSection *pSecond) { const SwSectionFormat* pFSectFormat = pFirst->GetFormat(); const SwSectionFormat* pSSectFormat = pSecond->GetFormat(); OSL_ENSURE( pFSectFormat && pSSectFormat && pFSectFormat->GetContent(false).GetContentIdx() && pSSectFormat->GetContent(false).GetContentIdx(), "Invalid sections" ); return pFSectFormat->GetContent(false).GetContentIdx()->GetIndex() < pSSectFormat->GetContent(false).GetContentIdx()->GetIndex(); } // get all Sections that have been derived from this one void SwSectionFormat::GetChildSections( SwSections& rArr, SectionSort eSort, bool bAllSections ) const { rArr.clear(); if( HasWriterListeners() ) { SwIterator aIter(*this); const SwNodeIndex* pIdx; for( SwSectionFormat* pLast = aIter.First(); pLast; pLast = aIter.Next() ) if( bAllSections || ( nullptr != ( pIdx = pLast->GetContent(false). GetContentIdx()) && &pIdx->GetNodes() == &GetDoc()->GetNodes() )) { SwSection* pDummy = pLast->GetSection(); rArr.push_back( pDummy ); } // Do we need any sorting? if( 1 < rArr.size() ) switch( eSort ) { case SectionSort::Pos: std::sort( rArr.begin(), rArr.end(), lcl_SectionCmpPos ); break; case SectionSort::Not: break; } } } // See whether the Section is within the Nodes or the UndoNodes array bool SwSectionFormat::IsInNodesArr() const { const SwNodeIndex* pIdx = GetContent(false).GetContentIdx(); return pIdx && &pIdx->GetNodes() == &GetDoc()->GetNodes(); } // Parent was changed void SwSectionFormat::UpdateParent() { if( !HasWriterListeners() ) return; SwSection* pSection = nullptr; const SvxProtectItem* pProtect(nullptr); // edit in readonly sections const SwFormatEditInReadonly* pEditInReadonly = nullptr; bool bIsHidden = false; SwIterator aIter(*this); for(SwClient* pLast = aIter.First(); pLast; pLast = aIter.Next()) { if( dynamic_cast(pLast) != nullptr ) { if( !pSection ) { pSection = GetSection(); if( GetRegisteredIn() ) { const SwSection* pPS = GetParentSection(); pProtect = &pPS->GetFormat()->GetProtect(); // edit in readonly sections pEditInReadonly = &pPS->GetFormat()->GetEditInReadonly(); bIsHidden = pPS->IsHiddenFlag(); } else { pProtect = &GetProtect(); // edit in readonly sections pEditInReadonly = &GetEditInReadonly(); bIsHidden = pSection->IsHidden(); } } if (!pProtect->IsContentProtected() != !pSection->IsProtectFlag()) { pLast->ModifyNotification( static_cast(pProtect), static_cast(pProtect) ); } // edit in readonly sections if (!pEditInReadonly->GetValue() != !pSection->IsEditInReadonlyFlag()) { pLast->ModifyNotification( static_cast(pEditInReadonly), static_cast(pEditInReadonly) ); } if( bIsHidden == pSection->IsHiddenFlag() ) { SwMsgPoolItem aMsgItem( static_cast(bIsHidden ? RES_SECTION_HIDDEN : RES_SECTION_NOT_HIDDEN ) ); pLast->ModifyNotification( &aMsgItem, &aMsgItem ); } } else if( !pSection && dynamic_cast(pLast) != nullptr ) { pSection = static_cast(pLast); if( GetRegisteredIn() ) { const SwSection* pPS = GetParentSection(); pProtect = &pPS->GetFormat()->GetProtect(); // edit in readonly sections pEditInReadonly = &pPS->GetFormat()->GetEditInReadonly(); bIsHidden = pPS->IsHiddenFlag(); } else { pProtect = &GetProtect(); // edit in readonly sections pEditInReadonly = &GetEditInReadonly(); bIsHidden = pSection->IsHidden(); } } } } SwSectionNode* SwSectionFormat::GetSectionNode() { const SwNodeIndex* pIdx = GetContent(false).GetContentIdx(); if( pIdx && ( &pIdx->GetNodes() == &GetDoc()->GetNodes() )) return pIdx->GetNode().GetSectionNode(); return nullptr; } // Is this Section valid for the GlobalDocument? const SwSection* SwSectionFormat::GetGlobalDocSection() const { const SwSectionNode* pNd = GetSectionNode(); if( pNd && ( SectionType::FileLink == pNd->GetSection().GetType() || SectionType::ToxContent == pNd->GetSection().GetType() ) && pNd->GetIndex() > pNd->GetNodes().GetEndOfExtras().GetIndex() && !pNd->StartOfSectionNode()->IsSectionNode() && !pNd->StartOfSectionNode()->FindSectionNode() ) return &pNd->GetSection(); return nullptr; } // sw::Metadatable ::sfx2::IXmlIdRegistry& SwSectionFormat::GetRegistry() { return GetDoc()->GetXmlIdRegistry(); } bool SwSectionFormat::IsInClipboard() const { return GetDoc()->IsClipBoard(); } bool SwSectionFormat::IsInUndo() const { return !IsInNodesArr(); } bool SwSectionFormat::IsInContent() const { SwNodeIndex const*const pIdx = GetContent(false).GetContentIdx(); OSL_ENSURE(pIdx, "SwSectionFormat::IsInContent: no index?"); return pIdx == nullptr || !GetDoc()->IsInHeaderFooter(*pIdx); } // n.b.: if the section format represents an index, then there is both a // SwXDocumentIndex and a SwXTextSection instance for this single core object. // these two can both implement XMetadatable and forward to the same core // section format. but here only one UNO object can be returned, // so always return the text section. uno::Reference< rdf::XMetadatable > SwSectionFormat::MakeUnoObject() { uno::Reference xMeta; SwSection *const pSection( GetSection() ); if (pSection) { xMeta.set( SwXTextSection::CreateXTextSection(this, SectionType::ToxHeader == pSection->GetType()), uno::UNO_QUERY ); } return xMeta; } bool SwSectionFormat::supportsFullDrawingLayerFillAttributeSet() const { return false; } void SwSectionFormat::dumpAsXml(xmlTextWriterPtr pWriter) const { xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormat")); xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr())); GetAttrSet().dumpAsXml(pWriter); xmlTextWriterEndElement(pWriter); } void SwSectionFormats::dumpAsXml(xmlTextWriterPtr pWriter) const { xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormats")); for (size_t i = 0; i < size(); ++i) GetFormat(i)->dumpAsXml(pWriter); xmlTextWriterEndElement(pWriter); } // Method to break section links inside a linked section static void lcl_BreakSectionLinksInSect( const SwSectionNode& rSectNd ) { if ( !rSectNd.GetDoc() ) { OSL_FAIL( "method - no Doc at SectionNode" ); return; } if ( !rSectNd.GetSection().IsConnected() ) { OSL_FAIL( "method - no Link at Section of SectionNode" ); return; } const ::sfx2::SvBaseLink* pOwnLink( &(rSectNd.GetSection().GetBaseLink() ) ); const ::sfx2::SvBaseLinks& rLnks = rSectNd.GetDoc()->getIDocumentLinksAdministration().GetLinkManager().GetLinks(); for ( auto n = rLnks.size(); n > 0; ) { SwIntrnlSectRefLink* pSectLnk = dynamic_cast(&(*rLnks[ --n ])); if ( pSectLnk && pSectLnk != pOwnLink && pSectLnk->IsInRange( rSectNd.GetIndex(), rSectNd.EndOfSectionIndex() ) ) { // break the link of the corresponding section. // the link is also removed from the link manager SwSectionNode* pSectNode = pSectLnk->GetSectNode(); assert(pSectNode); pSectNode->GetSection().BreakLink(); // for robustness, because link is removed from the link manager if ( n > rLnks.size() ) { n = rLnks.size(); } } } } static void lcl_UpdateLinksInSect( SwBaseLink& rUpdLnk, SwSectionNode& rSectNd ) { SwDoc* pDoc = rSectNd.GetDoc(); SwDocShell* pDShell = pDoc->GetDocShell(); if( !pDShell || !pDShell->GetMedium() ) return ; const OUString sName( pDShell->GetMedium()->GetName() ); const OUString sMimeType( SotExchange::GetFormatMimeType( SotClipboardFormatId::SIMPLE_FILE )); uno::Any aValue; aValue <<= sName; // Arbitrary name const ::sfx2::SvBaseLinks& rLnks = pDoc->getIDocumentLinksAdministration().GetLinkManager().GetLinks(); for( auto n = rLnks.size(); n; ) { SwBaseLink* pBLink; ::sfx2::SvBaseLink* pLnk = &(*rLnks[ --n ]); if( pLnk != &rUpdLnk && sfx2::SvBaseLinkObjectType::ClientFile == pLnk->GetObjType() && dynamic_cast< const SwBaseLink *>( pLnk ) != nullptr && ( pBLink = static_cast(pLnk) )->IsInRange( rSectNd.GetIndex(), rSectNd.EndOfSectionIndex() ) ) { // It's in the Section, so update. But only if it's not in the same File! OUString sFName; sfx2::LinkManager::GetDisplayNames( pBLink, nullptr, &sFName ); if( sFName != sName ) { pBLink->DataChanged( sMimeType, aValue ); // If needed find the Link pointer to avoid skipping one or calling one twice if( n >= rLnks.size() && 0 != ( n = rLnks.size() )) --n; if( n && pLnk != &(*rLnks[ n ]) ) { // Find - it can only precede it! while( n ) if( pLnk == &(*rLnks[ --n ] ) ) break; } } } } } ::sfx2::SvBaseLink::UpdateResult SwIntrnlSectRefLink::DataChanged( const OUString& rMimeType, const uno::Any & rValue ) { SwSectionNode* pSectNd = rSectFormat.GetSectionNode(); SwDoc* pDoc = rSectFormat.GetDoc(); SotClipboardFormatId nDataFormat = SotExchange::GetFormatIdFromMimeType( rMimeType ); if( !pSectNd || !pDoc || pDoc->IsInDtor() || ChkNoDataFlag() || sfx2::LinkManager::RegisterStatusInfoId() == nDataFormat ) { // Should we be in the Undo already? return SUCCESS; } // #i38810# - Due to possible existing signatures, the // document has to be modified after updating a link. pDoc->getIDocumentState().SetModified(); // set additional flag that links have been updated, in order to check this // during load. pDoc->getIDocumentLinksAdministration().SetLinksUpdated( true ); // Always switch off Undo bool const bWasUndo = pDoc->GetIDocumentUndoRedo().DoesUndo(); pDoc->GetIDocumentUndoRedo().DoUndo(false); bool bWasVisibleLinks = pDoc->getIDocumentLinksAdministration().IsVisibleLinks(); pDoc->getIDocumentLinksAdministration().SetVisibleLinks( false ); SwPaM* pPam; SwViewShell* pVSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); SwEditShell* pESh = pDoc->GetEditShell(); pDoc->getIDocumentFieldsAccess().LockExpFields(); { // Insert an empty TextNode at the Section's start SwNodeIndex aIdx( *pSectNd, +1 ); SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() ); SwTextNode* pNewNd = pDoc->GetNodes().MakeTextNode( aIdx, pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) ); if( pESh ) pESh->StartAllAction(); else if( pVSh ) pVSh->StartAction(); SwPosition aPos( aIdx, SwIndex( pNewNd, 0 )); --aPos.nNode; SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true ); pPam = new SwPaM( aPos ); // Delete everything succeeding it --aIdx; DelFlyInRange( aIdx, aEndIdx ); DelBookmarks(aIdx, aEndIdx); ++aIdx; pDoc->GetNodes().Delete( aIdx, aEndIdx.GetIndex() - aIdx.GetIndex() ); } SwSection& rSection = pSectNd->GetSection(); rSection.SetConnectFlag(false); Reader* pRead = nullptr; switch( nDataFormat ) { case SotClipboardFormatId::STRING: pRead = ReadAscii; break; case SotClipboardFormatId::RICHTEXT: case SotClipboardFormatId::RTF: pRead = SwReaderWriter::GetRtfReader(); break; case SotClipboardFormatId::SIMPLE_FILE: if ( rValue.hasValue() ) { OUString sFileName; if ( !(rValue >>= sFileName) ) break; OUString sFilter; OUString sRange; sfx2::LinkManager::GetDisplayNames( this, nullptr, &sFileName, &sRange, &sFilter ); RedlineFlags eOldRedlineFlags = RedlineFlags::NONE; SfxObjectShellRef xDocSh; SfxObjectShellLock xLockRef; int nRet; if( sFileName.isEmpty() ) { xDocSh = pDoc->GetDocShell(); nRet = 1; } else { nRet = SwFindDocShell( xDocSh, xLockRef, sFileName, rSection.GetLinkFilePassword(), sFilter, 0, pDoc->GetDocShell() ); if( nRet ) { SwDoc* pSrcDoc = static_cast( xDocSh.get() )->GetDoc(); eOldRedlineFlags = pSrcDoc->getIDocumentRedlineAccess().GetRedlineFlags(); pSrcDoc->getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowInsert ); } } if( nRet ) { rSection.SetConnectFlag(); SwNodeIndex aSave( pPam->GetPoint()->nNode, -1 ); std::unique_ptr pCpyRg; if( xDocSh->GetMedium() && rSection.GetLinkFilePassword().isEmpty() ) { const SfxPoolItem* pItem; if( SfxItemState::SET == xDocSh->GetMedium()->GetItemSet()-> GetItemState( SID_PASSWORD, false, &pItem ) ) rSection.SetLinkFilePassword( static_cast(pItem)->GetValue() ); } SwDoc* pSrcDoc = static_cast( xDocSh.get() )->GetDoc(); if( !sRange.isEmpty() ) { // Catch recursion bool bRecursion = false; if( pSrcDoc == pDoc ) { tools::SvRef refObj( static_cast( pDoc->getIDocumentLinksAdministration().CreateLinkSource( sRange ))); if( refObj.is() ) { bRecursion = refObj->IsLinkInServer( this ) || ChkNoDataFlag(); } } SwNodeIndex& rInsPos = pPam->GetPoint()->nNode; SwPaM* pCpyPam = nullptr; if( !bRecursion && pSrcDoc->GetDocumentLinksAdministrationManager().SelectServerObj( sRange, pCpyPam, pCpyRg ) && pCpyPam ) { if( pSrcDoc != pDoc || pCpyPam->Start()->nNode > rInsPos || rInsPos >= pCpyPam->End()->nNode ) { pSrcDoc->getIDocumentContentOperations().CopyRange(*pCpyPam, *pPam->GetPoint(), SwCopyFlags::CheckPosInFly); } delete pCpyPam; } if( pCpyRg && pSrcDoc == pDoc && pCpyRg->aStart < rInsPos && rInsPos < pCpyRg->aEnd ) { pCpyRg.reset(); } } else if( pSrcDoc != pDoc ) pCpyRg.reset(new SwNodeRange( pSrcDoc->GetNodes().GetEndOfExtras(), 2, pSrcDoc->GetNodes().GetEndOfContent() )); // #i81653# // Update links of extern linked document or extern linked // document section, if section is protected. if ( pSrcDoc != pDoc && rSection.IsProtectFlag() ) { pSrcDoc->getIDocumentLinksAdministration().GetLinkManager().UpdateAllLinks( false, false, nullptr ); } if( pCpyRg ) { SwNodeIndex& rInsPos = pPam->GetPoint()->nNode; bool bCreateFrame = rInsPos.GetIndex() <= pDoc->GetNodes().GetEndOfExtras().GetIndex() || rInsPos.GetNode().FindTableNode(); SwTableNumFormatMerge aTNFM( *pSrcDoc, *pDoc ); pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(*pCpyRg, rInsPos, nullptr, bCreateFrame); ++aSave; if( !bCreateFrame ) ::MakeFrames( pDoc, aSave, rInsPos ); // Delete last Node, only if it was copied successfully // (the Section contains more than one Node) if( 2 < pSectNd->EndOfSectionIndex() - pSectNd->GetIndex() ) { aSave = rInsPos; pPam->Move( fnMoveBackward, GoInNode ); pPam->SetMark(); // Rewire both SwPositions pDoc->CorrAbs( aSave, *pPam->GetPoint(), 0, true ); pDoc->GetNodes().Delete( aSave ); } pCpyRg.reset(); } lcl_BreakSectionLinksInSect( *pSectNd ); // Update all Links in this Section lcl_UpdateLinksInSect( *this, *pSectNd ); } if( xDocSh.is() ) { if( 2 == nRet ) xDocSh->DoClose(); else if( static_cast( xDocSh.get() )->GetDoc() ) static_cast( xDocSh.get() )->GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOldRedlineFlags ); } } break; default: break; } // Only create DDE if Shell is available! uno::Sequence< sal_Int8 > aSeq; if( pRead && rValue.hasValue() && ( rValue >>= aSeq ) ) { if( pESh ) { pESh->Push(); SwPaM* pCursor = pESh->GetCursor(); *pCursor->GetPoint() = *pPam->GetPoint(); delete pPam; pPam = pCursor; } SvMemoryStream aStrm( const_cast(aSeq.getConstArray()), aSeq.getLength(), StreamMode::READ ); aStrm.Seek( 0 ); // TODO/MBA: it's impossible to set a BaseURL here! SwReader aTmpReader( aStrm, OUString(), pDoc->GetDocShell()->GetMedium()->GetBaseURL(), *pPam ); if( ! aTmpReader.Read( *pRead ).IsError() ) { rSection.SetConnectFlag(); } if( pESh ) { pESh->Pop(SwCursorShell::PopMode::DeleteCurrent); pPam = nullptr; // pam was deleted earlier } } // remove all undo actions and turn undo on again pDoc->GetIDocumentUndoRedo().DelAllUndoObj(); pDoc->GetIDocumentUndoRedo().DoUndo(bWasUndo); pDoc->getIDocumentLinksAdministration().SetVisibleLinks( bWasVisibleLinks ); pDoc->getIDocumentFieldsAccess().UnlockExpFields(); if( !pDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() ) pDoc->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true); if( pESh ) pESh->EndAllAction(); else if( pVSh ) pVSh->EndAction(); delete pPam; // Was created at the start return SUCCESS; } void SwIntrnlSectRefLink::Closed() { SwDoc* pDoc = rSectFormat.GetDoc(); if( pDoc && !pDoc->IsInDtor() ) { // Advise says goodbye: mark the Section as not protected // and change the Flag const SwSectionFormats& rFormats = pDoc->GetSections(); for( auto n = rFormats.size(); n; ) if( rFormats[ --n ] == &rSectFormat ) { SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); SwEditShell* pESh = pDoc->GetEditShell(); if( pESh ) pESh->StartAllAction(); else pSh->StartAction(); SwSectionData aSectionData(*rSectFormat.GetSection()); aSectionData.SetType( SectionType::Content ); aSectionData.SetLinkFileName( OUString() ); aSectionData.SetProtectFlag( false ); // edit in readonly sections aSectionData.SetEditInReadonlyFlag( false ); aSectionData.SetConnectFlag( false ); pDoc->UpdateSection( n, aSectionData ); // Make all Links within the Section visible again SwSectionNode* pSectNd = rSectFormat.GetSectionNode(); if( pSectNd ) SwSection::MakeChildLinksVisible( *pSectNd ); if( pESh ) pESh->EndAllAction(); else pSh->EndAction(); break; } } SvBaseLink::Closed(); } void SwSection::CreateLink( LinkCreateType eCreateType ) { SwSectionFormat* pFormat = GetFormat(); OSL_ENSURE(pFormat, "SwSection::CreateLink: no format?"); if (!pFormat || (SectionType::Content == m_Data.GetType())) return ; SfxLinkUpdateMode nUpdateType = SfxLinkUpdateMode::ALWAYS; if (!m_RefLink.is()) { // create BaseLink m_RefLink = new SwIntrnlSectRefLink( *pFormat, nUpdateType ); } else { pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() ); } SwIntrnlSectRefLink *const pLnk = static_cast( m_RefLink.get() ); const OUString sCmd(SwSectionData::CollapseWhiteSpaces(m_Data.GetLinkFileName())); pLnk->SetUpdateMode( nUpdateType ); pLnk->SetVisible( pFormat->GetDoc()->getIDocumentLinksAdministration().IsVisibleLinks() ); switch (m_Data.GetType()) { case SectionType::DdeLink: pLnk->SetLinkSourceName( sCmd ); pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( pLnk ); break; case SectionType::FileLink: { pLnk->SetContentType( SotClipboardFormatId::SIMPLE_FILE ); sal_Int32 nIndex = 0; const OUString sFile(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex )); const OUString sFltr(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex )); const OUString sRange(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex )); pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *pLnk, static_cast(m_Data.GetType()), sFile, ( !sFltr.isEmpty() ? &sFltr : nullptr ), ( !sRange.isEmpty() ? &sRange : nullptr ) ); } break; default: OSL_ENSURE( false, "What kind of Link is this?" ); } switch( eCreateType ) { case LinkCreateType::Connect: // Connect Link right away pLnk->Connect(); break; case LinkCreateType::Update: // Connect Link and update pLnk->Update(); break; case LinkCreateType::NONE: break; } } void SwSection::BreakLink() { const SectionType eCurrentType( GetType() ); if ( eCurrentType == SectionType::Content || eCurrentType == SectionType::ToxHeader || eCurrentType == SectionType::ToxContent ) { // nothing to do return; } // Release link, if it exists if (m_RefLink.is()) { SwSectionFormat *const pFormat( GetFormat() ); OSL_ENSURE(pFormat, "SwSection::BreakLink: no format?"); if (pFormat) { pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() ); } m_RefLink.clear(); } // change type SetType( SectionType::Content ); // reset linked file data SetLinkFileName( OUString() ); SetLinkFilePassword( OUString() ); } const SwNode* SwIntrnlSectRefLink::GetAnchor() const { return rSectFormat.GetSectionNode(); } bool SwIntrnlSectRefLink::IsInRange( sal_uLong nSttNd, sal_uLong nEndNd ) const { SwStartNode* pSttNd = rSectFormat.GetSectionNode(); return pSttNd && nSttNd < pSttNd->GetIndex() && pSttNd->EndOfSectionIndex() < nEndNd; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */