/* -*- 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 .
*/
sal_Int8 SwDoc::SetFlyFrameAnchor( SwFrameFormat& rFormat, SfxItemSet& rSet, bool bNewFrames )
{ // Changing anchors is almost always allowed. // Exception: Paragraph and character bound frames must not become // page bound, if they are located in the header or footer. const SwFormatAnchor &rOldAnch = rFormat.GetAnchor(); const RndStdIds nOld = rOldAnch.GetAnchorId();
// Is the new anchor valid? if( !aNewAnch.GetAnchorNode() && (RndStdIds::FLY_AT_FLY == nNew ||
(RndStdIds::FLY_AT_PARA == nNew) || (RndStdIds::FLY_AS_CHAR == nNew) ||
(RndStdIds::FLY_AT_CHAR == nNew) ))
{ return IGNOREANCHOR;
}
if( nOld == nNew ) return DONTMAKEFRMS;
Point aOldAnchorPos( ::lcl_FindAnchorLayPos( *this, rOldAnch, &rFormat ));
Point aNewAnchorPos( ::lcl_FindAnchorLayPos( *this, aNewAnch, nullptr ));
// Destroy the old Frames. // The Views are hidden implicitly, so hiding them another time would be // kind of a show!
rFormat.DelFrames();
if ( RndStdIds::FLY_AS_CHAR == nOld )
{ // We need to handle InContents in a special way: // The TextAttribute needs to be destroyed which, unfortunately, also // destroys the format. To avoid that, we disconnect the format from // the attribute.
SwNode *pAnchorNode = rOldAnch.GetAnchorNode();
SwTextNode *pTextNode = pAnchorNode->GetTextNode();
OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." ); const sal_Int32 nIdx = rOldAnch.GetAnchorContentOffset();
SwTextAttr * const pHint =
pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
OSL_ENSURE( pHint && pHint->Which() == RES_TXTATR_FLYCNT, "Missing FlyInCnt-Hint." );
OSL_ENSURE( pHint && pHint->GetFlyCnt().GetFrameFormat() == &rFormat, "Wrong TextFlyCnt-Hint." ); if (pHint) const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
// They are disconnected. We now have to destroy the attribute.
pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
}
// We can finally set the attribute. It needs to be the first one! // Undo depends on it!
rFormat.SetFormatAttr( aNewAnch );
// Correct the position switch( nNew )
{ case RndStdIds::FLY_AS_CHAR: // If no position attributes are received, we have to make sure // that no forbidden automatic alignment is left.
{
SwNode *pAnchorNode = aNewAnch.GetAnchorNode();
SwTextNode *pNd = pAnchorNode->GetTextNode();
OSL_ENSURE( pNd, "Cursor does not point to TextNode." );
case RndStdIds::FLY_AT_PARA: case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL case RndStdIds::FLY_AT_FLY: // LAYER_IMPL case RndStdIds::FLY_AT_PAGE:
{ // If only the anchor type has changed (char -> para -> page) and the absolute position // is unchanged even though there is a new relative orientation // (likely because the old orientation was not valid for the new anchor type), // then adjust the position to account for the moved anchor position. const SwFormatHoriOrient* pHoriOrientItem = rSet.GetItemIfSet( RES_HORI_ORIENT, false );
staticbool
lcl_SetFlyFrameAttr(SwDoc & rDoc,
sal_Int8 (SwDoc::*pSetFlyFrameAnchor)(SwFrameFormat &, SfxItemSet &, bool),
SwFrameFormat & rFlyFormat, SfxItemSet & rSet)
{ // #i32968# Inserting columns in the frame causes MakeFrameFormat to put two // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
// Is the anchor attribute included? // If so, we pass it to a special method, which returns true // if the Fly needs to be created anew, because we e.g change the FlyType.
sal_Int8 const nMakeFrames =
(SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
? (rDoc.*pSetFlyFrameAnchor)( rFlyFormat, rSet, false )
: DONTMAKEFRMS;
SwUndoSetFlyFormat* pUndo = nullptr; boolconst bUndo = GetIDocumentUndoRedo().DoesUndo(); if (bUndo)
{
pUndo = new SwUndoSetFlyFormat( rFormat, rNewFormat );
GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
}
// #i32968# Inserting columns in the section causes MakeFrameFormat to put // 2 objects of type SwUndoFrameFormat on the undo stack. We don't want them.
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
// Set the column first, or we'll have trouble with //Set/Reset/Synch. and so on if( SfxItemState::SET != rNewFormat.GetAttrSet().GetItemState( RES_COL ))
rFormat.ResetFormatAttr( RES_COL );
// 1. If not automatic = ignore; else = dispose // 2. Dispose of it! if( SfxItemState::SET == rNewFormat.GetAttrSet().GetItemState( RES_FRM_SIZE, false ))
{
rFormat.ResetFormatAttr( RES_FRM_SIZE );
bFrameSz = true;
}
const SfxItemSet* pAsk = pSet; if( !pAsk ) pAsk = &rNewFormat.GetAttrSet(); const SwFormatAnchor* pFormatAnchor = pAsk->GetItemIfSet( RES_ANCHOR, false ); if( pFormatAnchor
&& pFormatAnchor->GetAnchorId() !=
rFormat.GetAnchor().GetAnchorId() )
{ if( pSet )
bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, *pSet, false ); else
{ // Needs to have the FlyFormat range, because we set attributes in it, // in SetFlyFrameAnchor.
SfxItemSet aFlySet( *rNewFormat.GetAttrSet().GetPool(),
rNewFormat.GetAttrSet().GetRanges() );
aFlySet.Put( *pFormatAnchor );
bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, aFlySet, false);
}
}
}
// Only reset vertical and horizontal orientation, if we have automatic alignment // set in the template. Otherwise use the old value. // If we update the frame template the Fly should NOT lose its orientation (which // is not being updated!). // text::HoriOrientation::NONE and text::VertOrientation::NONE are allowed now if (!bKeepOrient)
{
rFormat.ResetFormatAttr(RES_VERT_ORIENT);
rFormat.ResetFormatAttr(RES_HORI_ORIENT);
}
bool bUnmark = false; for ( size_t i = 0; i < _rMrkList.GetMarkCount(); ++i )
{
SdrObject* pObj = _rMrkList.GetMark( i )->GetMarkedSdrObj(); if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
{
SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
// consider, that drawing object has // no user call. E.g.: a 'virtual' drawing object is disconnected by // the anchor type change of the 'master' drawing object. // Continue with next selected object and assert, if this isn't excepted. if ( !pContact )
{ #if OSL_DEBUG_LEVEL > 0 auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj); bool bNoUserCallExcepted = pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected();
OSL_ENSURE( bNoUserCallExcepted, "SwDoc::ChgAnchor(..) - no contact at selected drawing object" ); #endif continue;
}
// #i54336# // Instead of only keeping the index position for an as-character // anchored object the complete <SwPosition> is kept, because the // anchor index position could be moved, if the object again is // anchored as character.
std::optional<SwPosition> oOldAsCharAnchorPos; const RndStdIds eOldAnchorType = pContact->GetAnchorId(); if ( !_bSameOnly && eOldAnchorType == RndStdIds::FLY_AS_CHAR )
{
oOldAsCharAnchorPos.emplace(*pContact->GetAnchorFormat().GetContentAnchor());
}
// Has a textbox attached to the format? Sync it as well! if (pContact->GetFormat() && pContact->GetFormat()->GetOtherTextBoxFormats())
{
SwTextBoxHelper::synchronizeGroupTextBoxProperty(
SwTextBoxHelper::changeAnchor, pContact->GetFormat(), pObj);
}
} break; default:
OSL_ENSURE( false, "unexpected AnchorId." );
}
if ( (RndStdIds::FLY_AS_CHAR != _eAnchorType) &&
pNewAnchorFrame &&
( !_bSameOnly || pNewAnchorFrame != pOldAnchorFrame ) )
{ // #i26791# - Direct object positioning no longer needed. Apply // of attributes (method call <SetAttr(..)>) takes care of the // invalidation of the object position. if ( _bPosCorr )
{ // #i33313# - consider not connected 'virtual' drawing // objects auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj); if ( pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected() )
{
SwRect aNewObjRect( aObjRect ); static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( nullptr ))
->AdjustPositioningAttr( pNewAnchorFrame,
&aNewObjRect );
} else
{ static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ))
->AdjustPositioningAttr( pNewAnchorFrame );
}
} if (aNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
{
SwFormatHoriOrient item(pContact->GetFormat()->GetHoriOrient());
sal_Int16 nRelOrient(item.GetRelationOrient()); if (sw::GetAtPageRelOrientation(nRelOrient, false))
{
SAL_INFO("sw.ui", "fixing horizontal RelOrientation for at-page anchor");
item.SetRelationOrient(nRelOrient);
SetAttr(item, *pContact->GetFormat());
}
} // tdf#136385 set the anchor last - otherwise it messes up the // position in SwDrawContact::Changed_() callback
SetAttr(aNewAnch, *pContact->GetFormat());
}
// we have changed the anchoring attributes, and those are used to // order the object in its sorted list, so update its position
pAnchoredObj->UpdateObjInSortedList();
// #i54336# if (oOldAsCharAnchorPos)
{ if ( pNewAnchorFrame)
{ // We need to handle InContents in a special way: // The TextAttribute needs to be destroyed which, unfortunately, also // destroys the format. To avoid that, we disconnect the format from // the attribute. const sal_Int32 nIndx( oOldAsCharAnchorPos->GetContentIndex() );
SwTextNode* pTextNode( oOldAsCharAnchorPos->GetNode().GetTextNode() );
assert(pTextNode && " - missing previous anchor text node for as-character anchored object");
SwTextAttr * const pHint =
pTextNode->GetTextAttrForCharAt( nIndx, RES_TXTATR_FLYCNT );
assert(pHint && "Missing FlyInCnt-Hint."); const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
// They are disconnected. We now have to destroy the attribute.
pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIndx, nIndx );
}
}
}
}
SwChainRet SwDoc::Chainable( const SwFrameFormat &rSource, const SwFrameFormat &rDest )
{ // The Source must not yet have a Follow. const SwFormatChain &rOldChain = rSource.GetChain(); if ( rOldChain.GetNext() ) return SwChainRet::SOURCE_CHAINED;
// Target must not be equal to Source and we also must not have a closed chain. const SwFrameFormat *pFormat = &rDest; do { if( pFormat == &rSource ) return SwChainRet::SELF;
pFormat = pFormat->GetChain().GetNext();
} while ( pFormat );
// There must not be a chaining from outside to inside or the other way around. if( rDest.IsLowerOf( rSource ) || rSource .IsLowerOf( rDest ) ) return SwChainRet::SELF;
// The Target must not yet have a Master. const SwFormatChain &rChain = rDest.GetChain(); if( rChain.GetPrev() ) return SwChainRet::IS_IN_CHAIN;
// Split flys are incompatible with chaining. const SwFormatFlySplit& rOldSplit = rSource.GetFlySplit(); if (rOldSplit.GetValue())
{ return SwChainRet::SOURCE_CHAINED;
} const SwFormatFlySplit& rNewSplit = rDest.GetFlySplit(); if (rNewSplit.GetValue())
{
return SwChainRet::IS_IN_CHAIN;
}
// Target must be empty.
const SwNodeIndex* pCntIdx = rDest.GetContent().GetContentIdx();
if( !pCntIdx )
return SwChainRet::NOT_FOUND;
// We also need to consider the right area.
// Both Flys need to be located in the same area (Body, Header/Footer, Fly).
// If the Source is not the selected frame, it's enough to find a suitable
// one. e.g. if it's requested by the API.
// Attach Follow to the Master.
aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
SetAttr( aChain, rDestFormat );
// Attach Master to the Follow.
// Make sure that the Master has a fixed height.
aChain = rSource.GetChain();
aChain.SetNext( &rDestFormat );
aSet.Put( aChain );
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.