1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
22 #include <IDocumentUndoRedo.hxx>
23 #include <IDocumentLayoutAccess.hxx>
28 #include <fmtflcnt.hxx>
29 #include <txtflcnt.hxx>
30 #include <fmtanchr.hxx>
32 #include <flyfrms.hxx>
33 #include <objectformatter.hxx>
35 #include <dcontact.hxx>
36 #include <textboxhelper.hxx>
37 #include <osl/diagnose.h>
39 SwFormatFlyCnt::SwFormatFlyCnt( SwFrameFormat
*pFrameFormat
)
40 : SfxPoolItem( RES_TXTATR_FLYCNT
),
41 m_pTextAttr( nullptr ),
42 m_pFormat( pFrameFormat
)
46 bool SwFormatFlyCnt::operator==( const SfxPoolItem
& rAttr
) const
48 assert(SfxPoolItem::operator==(rAttr
));
49 return( m_pTextAttr
&& static_cast<const SwFormatFlyCnt
&>(rAttr
).m_pTextAttr
&&
50 m_pTextAttr
->GetStart() == static_cast<const SwFormatFlyCnt
&>(rAttr
).m_pTextAttr
->GetStart() &&
51 m_pFormat
== static_cast<const SwFormatFlyCnt
&>(rAttr
).GetFrameFormat() );
54 SwFormatFlyCnt
* SwFormatFlyCnt::Clone( SfxItemPool
* ) const
56 return new SwFormatFlyCnt( m_pFormat
);
59 void SwFormatFlyCnt::dumpAsXml(xmlTextWriterPtr pWriter
) const
61 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwFormatFlyCnt"));
62 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
63 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("text-attr"), "%p", m_pTextAttr
);
64 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("format"), "%p", m_pFormat
);
66 SfxPoolItem::dumpAsXml(pWriter
);
68 (void)xmlTextWriterEndElement(pWriter
);
71 SwTextFlyCnt::SwTextFlyCnt( SwFormatFlyCnt
& rAttr
, sal_Int32 nStartPos
)
72 : SwTextAttr( rAttr
, nStartPos
)
74 rAttr
.m_pTextAttr
= this;
75 SetHasDummyChar(true);
78 /** An overview of how a new SwTextFlyCnt is created:
79 * MakeTextAttr() is called e.g. by SwTextNode::CopyText().
80 * The following steps are required to clone:
81 * 1) copying the pFormat with content, attributes etc.
82 * 2) setting the anchor
84 * Because not all required information is available at all times,
85 * the steps are distributed variously:
86 * ad 1) MakeTextAttr() calls DocumentLayoutManager::CopyLayoutFormat()
87 * which creates the new SwFlyFrameFormat and copies the content of the
89 * ad 2) SetAnchor() is called by SwTextNode::InsertHint() and sets the anchor
90 * position in the SwFlyFrameFormat to the SwPosition of the dummy
91 * CH_TXTATR_BREAKWORD. This cannot be done in MakeTextAttr() because it
92 * doesn't know the target text node.
93 * ad 3) GetFlyFrame_() is called during text formatting by SwTextFormatter
94 * and searches for the SwFlyFrame for the dummy char of the current
95 * SwTextFrame. If none is found, a new SwFlyInContentFrame is created.
96 * Important: pTextFrame->AppendFly() immediately triggers a reformat
97 * of pTextFrame. However, the recursion is blocked by the lock mechanism
98 * in SwTextFrame::Format().
99 * The advantage of all this is that it's not necessary to explicitly iterate
100 * over all SwTextFrames that depend on the SwTextNode to create the
101 * SwFlyInContentFrame - this is done automatically already.
104 void SwTextFlyCnt::CopyFlyFormat( SwDoc
& rDoc
)
106 SwFrameFormat
* pFormat
= GetFlyCnt().GetFrameFormat();
108 // The FlyFrameFormat must be copied - CopyLayoutFormat
109 // (DocumentLayoutManager.cxx) creates the FlyFrameFormat and copies the
112 // disable undo while copying attribute
113 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
114 SwFormatAnchor
aAnchor( pFormat
->GetAnchor() );
115 if ((RndStdIds::FLY_AT_PAGE
!= aAnchor
.GetAnchorId()) &&
116 (&rDoc
!= pFormat
->GetDoc())) // different documents?
118 // JP 03.06.96: ensure that the copied anchor points to valid content!
119 // setting it to the correct position is done later.
120 SwNodeIndex
aIdx( rDoc
.GetNodes().GetEndOfExtras(), +2 );
121 SwContentNode
* pCNd
= aIdx
.GetNode().GetContentNode();
123 pCNd
= rDoc
.GetNodes().GoNext( &aIdx
);
125 SwPosition
pos(aIdx
.GetNode());
126 aAnchor
.SetAnchor( &pos
);
129 SwFrameFormat
* pNew
= rDoc
.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat
, aAnchor
, false, false );
130 const_cast<SwFormatFlyCnt
&>(GetFlyCnt()).SetFlyFormat( pNew
);
133 /** SetAnchor() is called by SwTextNode::InsertHint() and sets the anchor
134 * position in the SwFlyFrameFormat to the SwPosition of the dummy
135 * CH_TXTATR_BREAKWORD. This cannot be done in MakeTextAttr() because it
136 * doesn't know the target text node.
138 void SwTextFlyCnt::SetAnchor( const SwTextNode
*pNode
)
140 // for Undo, the new anchor must be known already!
142 SwDoc
& rDoc
= const_cast<SwDoc
&>(pNode
->GetDoc());
144 SwFrameFormat
* pFormat
= GetFlyCnt().GetFrameFormat();
145 SwFormatAnchor
aAnchor( pFormat
->GetAnchor() );
146 SwNode
*const pOldNode(aAnchor
.GetAnchorNode());
148 std::optional
<SwPosition
> oPos
;
149 if (!pOldNode
|| !pOldNode
->GetNodes().IsDocNodes() ||
150 pOldNode
!= static_cast<SwNode
const *>(pNode
))
152 oPos
.emplace( *pNode
, GetStart() );
156 oPos
.emplace( *pOldNode
, pOldNode
->GetContentNode(), GetStart() );
159 aAnchor
.SetType( RndStdIds::FLY_AS_CHAR
); // default!
160 aAnchor
.SetAnchor( &*oPos
);
162 // in case of anchor change, delete all FlyFrames
163 // JP 25.04.95: if the Frames can be moved within SplitNode, they don't
164 // need to be deleted
165 if( ( !pNode
->GetpSwpHints() || !pNode
->GetpSwpHints()->IsInSplitNode() )
166 && RES_DRAWFRMFMT
!= pFormat
->Which() )
167 pFormat
->DelFrames();
169 // copy into a different document?
170 if( &rDoc
!= pFormat
->GetDoc() )
172 // disable undo while copying attribute
173 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
174 SwFrameFormat
* pNew
= rDoc
.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat
, aAnchor
, false, false );
176 ::sw::UndoGuard
const undoGuardFormat(
177 pFormat
->GetDoc()->GetIDocumentUndoRedo());
178 pFormat
->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pFormat
);
179 const_cast<SwFormatFlyCnt
&>(GetFlyCnt()).SetFlyFormat( pNew
);
181 else if( pNode
->GetpSwpHints() &&
182 pNode
->GetpSwpHints()->IsInSplitNode() &&
183 RES_DRAWFRMFMT
!= pFormat
->Which() )
185 pFormat
->LockModify();
186 pFormat
->SetFormatAttr( aAnchor
); // only set the anchor
187 // tdf#91228 must notify the anchor nodes despite LockModify
189 pOldNode
->RemoveAnchoredFly(pFormat
);
190 oPos
->GetNode().AddAnchoredFly(pFormat
);
191 pFormat
->UnlockModify();
195 assert(!pFormat
->IsModifyLocked()); // need to notify anchor node
196 if (RES_DRAWFRMFMT
== pFormat
->Which())
198 if (SdrObject
const*const pObj
= pFormat
->FindSdrObject())
199 { // tdf#123259 disconnect with *old* anchor position
200 static_cast<SwDrawContact
*>(::GetUserCall(pObj
))->DisconnectFromLayout(false);
203 pFormat
->SetFormatAttr( aAnchor
); // only set the anchor
205 // If the draw format has a TextBox, then set its anchor as well.
206 if (SwFrameFormat
* pTextBox
207 = SwTextBoxHelper::getOtherTextBoxFormat(pFormat
, RES_DRAWFRMFMT
))
209 SwFormatAnchor
aTextBoxAnchor(pTextBox
->GetAnchor());
210 aTextBoxAnchor
.SetAnchor(aAnchor
.GetContentAnchor());
212 // SwFlyAtContentFrame::SwClientNotify() assumes the anchor has a matching layout frame, which
213 // may not be the case when we're in the process of a node split, so block
215 bool bIsInSplitNode
= pNode
->GetpSwpHints() && pNode
->GetpSwpHints()->IsInSplitNode();
218 pTextBox
->LockModify();
222 // Otherwise delete fly frames on anchor change.
223 pTextBox
->DelFrames();
226 pTextBox
->SetFormatAttr(aTextBoxAnchor
);
230 pOldNode
->RemoveAnchoredFly(pTextBox
);
231 oPos
->GetNode().AddAnchoredFly(pTextBox
);
232 pTextBox
->UnlockModify();
236 pTextBox
->MakeFrames();
241 // The node may have several SwTextFrames - for every SwTextFrame a
242 // SwFlyInContentFrame is created.
246 /** GetFlyFrame_() is called during text formatting by SwTextFormatter
247 * and searches for the SwFlyFrame for the dummy char of the current
248 * SwTextFrame. If none is found, a new SwFlyInContentFrame is created.
250 SwFlyInContentFrame
*SwTextFlyCnt::GetFlyFrame_( const SwFrame
*pCurrFrame
)
252 SwFrameFormat
* pFrameFormat
= GetFlyCnt().GetFrameFormat();
253 if( RES_DRAWFRMFMT
== pFrameFormat
->Which() )
255 OSL_ENSURE( false, "SwTextFlyCnt::GetFlyFrame_: DrawInCnt-under construction!" );
259 SwIterator
<SwFlyFrame
,SwFormat
> aIter( *GetFlyCnt().m_pFormat
);
260 assert(pCurrFrame
->IsTextFrame());
261 SwFrame
* pFrame
= aIter
.First();
264 SwTextFrame
*pFirst
= const_cast<SwTextFrame
*>(static_cast<const SwTextFrame
*>(pCurrFrame
));
265 while ( pFirst
->IsFollow() )
266 pFirst
= pFirst
->FindMaster();
269 SwTextFrame
*pTmp
= pFirst
;
271 { if( static_cast<SwFlyFrame
*>(pFrame
)->GetAnchorFrame() == static_cast<SwFrame
*>(pTmp
) )
273 if ( pTmp
!= pCurrFrame
)
275 pTmp
->RemoveFly( static_cast<SwFlyFrame
*>(pFrame
) );
276 const_cast<SwTextFrame
*>(static_cast<const SwTextFrame
*>(pCurrFrame
))->AppendFly( static_cast<SwFlyFrame
*>(pFrame
) );
278 return static_cast<SwFlyInContentFrame
*>(pFrame
);
280 pTmp
= pTmp
->GetFollow();
283 pFrame
= aIter
.Next();
288 // We did not find a matching FlyFrame, so create a new one.
289 // AppendFly() triggers a reformat of pCurrentFrame. However, the
290 // recursion is blocked by the lock mechanism in SwTextFrame::Format().
291 SwFrame
* pCurrentFrame
= const_cast<SwFrame
*>(pCurrFrame
);
292 SwFlyInContentFrame
*pFly
= new SwFlyInContentFrame(static_cast<SwFlyFrameFormat
*>(pFrameFormat
), pCurrentFrame
, pCurrentFrame
);
293 pCurrentFrame
->AppendFly(pFly
);
296 // We must ensure that the content of the FlyInCnt is fully formatted
297 // right after construction.
298 // #i26945# - Use new object formatter to format Writer
299 // fly frame and its content.
300 SwObjectFormatter::FormatObj( *pFly
, const_cast<SwFrame
*>(pCurrFrame
),
301 pCurrFrame
->FindPageFrame() );
306 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */