1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <hintids.hxx>
21 #include <svl/itemiter.hxx>
22 #include <svx/svdobj.hxx>
23 #include <svx/svdmark.hxx>
24 #include <fmtfsize.hxx>
25 #include <fmtornt.hxx>
26 #include <dcontact.hxx>
27 #include <ndgrf.hxx>
28 #include <doc.hxx>
29 #include <IDocumentUndoRedo.hxx>
30 #include <IDocumentDrawModelAccess.hxx>
31 #include <IDocumentState.hxx>
32 #include <IDocumentLayoutAccess.hxx>
33 #include <ndindex.hxx>
34 #include <docary.hxx>
35 #include <drawdoc.hxx>
36 #include <fmtcntnt.hxx>
37 #include <fmtanchr.hxx>
38 #include <fmtflcnt.hxx>
39 #include <txtfrm.hxx>
40 #include <notxtfrm.hxx>
41 #include <pagefrm.hxx>
42 #include <rootfrm.hxx>
43 #include <flyfrm.hxx>
44 #include <textboxhelper.hxx>
45 #include <txatbase.hxx>
46 #include <frmfmt.hxx>
47 #include <ndtxt.hxx>
48 #include <pam.hxx>
49 #include <swundo.hxx>
50 #include <crstate.hxx>
51 #include <UndoCore.hxx>
52 #include <UndoAttribute.hxx>
53 #include <fmtcnct.hxx>
54 #include <dflyobj.hxx>
55 #include <undoflystrattr.hxx>
56 #include <calbck.hxx>
57 #include <memory>
58 #include <svx/xbtmpit.hxx>
59 #include <svx/xflftrit.hxx>
60 #include <svx/xlndsit.hxx>
61 #include <svx/xlnstit.hxx>
62 #include <svx/xlnedit.hxx>
63 #include <svx/xflhtit.hxx>
64
65 using namespace ::com::sun::star;
66
GetFlyCount(FlyCntType eType,bool bIgnoreTextBoxes) const67 size_t SwDoc::GetFlyCount( FlyCntType eType, bool bIgnoreTextBoxes ) const
68 {
69 const SwFrameFormats& rFormats = *GetSpzFrameFormats();
70 const size_t nSize = rFormats.size();
71 size_t nCount = 0;
72 const SwNodeIndex* pIdx;
73
74 for ( size_t i = 0; i < nSize; ++i)
75 {
76 const SwFrameFormat* pFlyFormat = rFormats[ i ];
77
78 if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
79 continue;
80
81 if( RES_FLYFRMFMT == pFlyFormat->Which()
82 && nullptr != ( pIdx = pFlyFormat->GetContent().GetContentIdx() )
83 && pIdx->GetNodes().IsDocNodes()
84 )
85 {
86 const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
87
88 switch( eType )
89 {
90 case FLYCNTTYPE_FRM:
91 if(!pNd->IsNoTextNode())
92 nCount++;
93 break;
94
95 case FLYCNTTYPE_GRF:
96 if( pNd->IsGrfNode() )
97 nCount++;
98 break;
99
100 case FLYCNTTYPE_OLE:
101 if(pNd->IsOLENode())
102 nCount++;
103 break;
104
105 default:
106 nCount++;
107 }
108 }
109 }
110 return nCount;
111 }
112
113 /// @attention If you change this, also update SwXFrameEnumeration in unocoll.
GetFlyNum(size_t nIdx,FlyCntType eType,bool bIgnoreTextBoxes)114 SwFrameFormat* SwDoc::GetFlyNum( size_t nIdx, FlyCntType eType, bool bIgnoreTextBoxes )
115 {
116 SwFrameFormats& rFormats = *GetSpzFrameFormats();
117 SwFrameFormat* pRetFormat = nullptr;
118 const size_t nSize = rFormats.size();
119 const SwNodeIndex* pIdx;
120 size_t nCount = 0;
121
122 for( size_t i = 0; !pRetFormat && i < nSize; ++i )
123 {
124 SwFrameFormat* pFlyFormat = rFormats[ i ];
125
126 if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
127 continue;
128
129 if( RES_FLYFRMFMT == pFlyFormat->Which()
130 && nullptr != ( pIdx = pFlyFormat->GetContent().GetContentIdx() )
131 && pIdx->GetNodes().IsDocNodes()
132 )
133 {
134 const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
135 switch( eType )
136 {
137 case FLYCNTTYPE_FRM:
138 if( !pNd->IsNoTextNode() && nIdx == nCount++)
139 pRetFormat = pFlyFormat;
140 break;
141 case FLYCNTTYPE_GRF:
142 if(pNd->IsGrfNode() && nIdx == nCount++ )
143 pRetFormat = pFlyFormat;
144 break;
145 case FLYCNTTYPE_OLE:
146 if(pNd->IsOLENode() && nIdx == nCount++)
147 pRetFormat = pFlyFormat;
148 break;
149 default:
150 if(nIdx == nCount++)
151 pRetFormat = pFlyFormat;
152 }
153 }
154 }
155 return pRetFormat;
156 }
157
GetFlyFrameFormats(FlyCntType const eType,bool const bIgnoreTextBoxes)158 std::vector<SwFrameFormat const*> SwDoc::GetFlyFrameFormats(
159 FlyCntType const eType, bool const bIgnoreTextBoxes)
160 {
161 SwFrameFormats& rFormats = *GetSpzFrameFormats();
162 const size_t nSize = rFormats.size();
163
164 std::vector<SwFrameFormat const*> ret;
165 ret.reserve(nSize);
166
167 for (size_t i = 0; i < nSize; ++i)
168 {
169 SwFrameFormat const*const pFlyFormat = rFormats[ i ];
170
171 if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
172 {
173 continue;
174 }
175
176 if (RES_FLYFRMFMT != pFlyFormat->Which())
177 {
178 continue;
179 }
180
181 SwNodeIndex const*const pIdx(pFlyFormat->GetContent().GetContentIdx());
182 if (pIdx && pIdx->GetNodes().IsDocNodes())
183 {
184 SwNode const*const pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
185 switch (eType)
186 {
187 case FLYCNTTYPE_FRM:
188 if (!pNd->IsNoTextNode())
189 ret.push_back(pFlyFormat);
190 break;
191 case FLYCNTTYPE_GRF:
192 if (pNd->IsGrfNode())
193 ret.push_back(pFlyFormat);
194 break;
195 case FLYCNTTYPE_OLE:
196 if (pNd->IsOLENode())
197 ret.push_back(pFlyFormat);
198 break;
199 default:
200 ret.push_back(pFlyFormat);
201 }
202 }
203 }
204
205 return ret;
206 }
207
lcl_FindAnchorLayPos(SwDoc & rDoc,const SwFormatAnchor & rAnch,const SwFrameFormat * pFlyFormat)208 static Point lcl_FindAnchorLayPos( SwDoc& rDoc, const SwFormatAnchor& rAnch,
209 const SwFrameFormat* pFlyFormat )
210 {
211 Point aRet;
212 if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
213 switch( rAnch.GetAnchorId() )
214 {
215 case RndStdIds::FLY_AS_CHAR:
216 if( pFlyFormat && rAnch.GetContentAnchor() )
217 {
218 const SwFrame* pOld = static_cast<const SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aRet );
219 if( pOld )
220 aRet = pOld->getFrameArea().Pos();
221 }
222 break;
223
224 case RndStdIds::FLY_AT_PARA:
225 case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
226 if( rAnch.GetContentAnchor() )
227 {
228 const SwPosition *pPos = rAnch.GetContentAnchor();
229 const SwContentNode* pNd = pPos->nNode.GetNode().GetContentNode();
230 std::pair<Point, bool> const tmp(aRet, false);
231 const SwFrame* pOld = pNd ? pNd->getLayoutFrame(rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
232 if( pOld )
233 aRet = pOld->getFrameArea().Pos();
234 }
235 break;
236
237 case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
238 if( rAnch.GetContentAnchor() )
239 {
240 const SwFlyFrameFormat* pFormat = static_cast<SwFlyFrameFormat*>(rAnch.GetContentAnchor()->
241 nNode.GetNode().GetFlyFormat());
242 const SwFrame* pOld = pFormat ? pFormat->GetFrame( &aRet ) : nullptr;
243 if( pOld )
244 aRet = pOld->getFrameArea().Pos();
245 }
246 break;
247
248 case RndStdIds::FLY_AT_PAGE:
249 {
250 sal_uInt16 nPgNum = rAnch.GetPageNum();
251 const SwPageFrame *pPage = static_cast<SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower());
252 for( sal_uInt16 i = 1; (i <= nPgNum) && pPage; ++i,
253 pPage =static_cast<const SwPageFrame*>(pPage->GetNext()) )
254 if( i == nPgNum )
255 {
256 aRet = pPage->getFrameArea().Pos();
257 break;
258 }
259 }
260 break;
261 default:
262 break;
263 }
264 return aRet;
265 }
266
267 #define MAKEFRMS 0
268 #define IGNOREANCHOR 1
269 #define DONTMAKEFRMS 2
270
SetFlyFrameAnchor(SwFrameFormat & rFormat,SfxItemSet & rSet,bool bNewFrames)271 sal_Int8 SwDoc::SetFlyFrameAnchor( SwFrameFormat& rFormat, SfxItemSet& rSet, bool bNewFrames )
272 {
273 // Changing anchors is almost always allowed.
274 // Exception: Paragraph and character bound frames must not become
275 // page bound, if they are located in the header or footer.
276 const SwFormatAnchor &rOldAnch = rFormat.GetAnchor();
277 const RndStdIds nOld = rOldAnch.GetAnchorId();
278
279 SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) );
280 RndStdIds nNew = aNewAnch.GetAnchorId();
281
282 // Is the new anchor valid?
283 if( !aNewAnch.GetContentAnchor() && (RndStdIds::FLY_AT_FLY == nNew ||
284 (RndStdIds::FLY_AT_PARA == nNew) || (RndStdIds::FLY_AS_CHAR == nNew) ||
285 (RndStdIds::FLY_AT_CHAR == nNew) ))
286 {
287 return IGNOREANCHOR;
288 }
289
290 if( nOld == nNew )
291 return DONTMAKEFRMS;
292
293 Point aOldAnchorPos( ::lcl_FindAnchorLayPos( *this, rOldAnch, &rFormat ));
294 Point aNewAnchorPos( ::lcl_FindAnchorLayPos( *this, aNewAnch, nullptr ));
295
296 // Destroy the old Frames.
297 // The Views are hidden implicitly, so hiding them another time would be
298 // kind of a show!
299 rFormat.DelFrames();
300
301 if ( RndStdIds::FLY_AS_CHAR == nOld )
302 {
303 // We need to handle InContents in a special way:
304 // The TextAttribut needs to be destroyed which, unfortunately, also
305 // destroys the format. To avoid that, we disconnect the format from
306 // the attribute.
307 const SwPosition *pPos = rOldAnch.GetContentAnchor();
308 SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
309 OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
310 const sal_Int32 nIdx = pPos->nContent.GetIndex();
311 SwTextAttr * const pHint =
312 pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
313 OSL_ENSURE( pHint && pHint->Which() == RES_TXTATR_FLYCNT,
314 "Missing FlyInCnt-Hint." );
315 OSL_ENSURE( pHint && pHint->GetFlyCnt().GetFrameFormat() == &rFormat,
316 "Wrong TextFlyCnt-Hint." );
317 if (pHint)
318 const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
319
320 // They are disconnected. We now have to destroy the attribute.
321 pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
322 }
323
324 // We can finally set the attribute. It needs to be the first one!
325 // Undo depends on it!
326 rFormat.SetFormatAttr( aNewAnch );
327
328 // Correct the position
329 const SfxPoolItem* pItem;
330 switch( nNew )
331 {
332 case RndStdIds::FLY_AS_CHAR:
333 // If no position attributes are received, we have to make sure
334 // that no forbidden automatic alignment is left.
335 {
336 const SwPosition *pPos = aNewAnch.GetContentAnchor();
337 SwTextNode *pNd = pPos->nNode.GetNode().GetTextNode();
338 OSL_ENSURE( pNd, "Cursor does not point to TextNode." );
339
340 SwFormatFlyCnt aFormat( static_cast<SwFlyFrameFormat*>(&rFormat) );
341 pNd->InsertItem( aFormat, pPos->nContent.GetIndex(), 0 );
342 }
343
344 if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false, &pItem ))
345 {
346 SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
347 bool bSet = true;
348 switch( aOldV.GetVertOrient() )
349 {
350 case text::VertOrientation::LINE_TOP: aOldV.SetVertOrient( text::VertOrientation::TOP ); break;
351 case text::VertOrientation::LINE_CENTER: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
352 case text::VertOrientation::LINE_BOTTOM: aOldV.SetVertOrient( text::VertOrientation::BOTTOM); break;
353 case text::VertOrientation::NONE: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
354 default:
355 bSet = false;
356 }
357 if( bSet )
358 rSet.Put( aOldV );
359 }
360 break;
361
362 case RndStdIds::FLY_AT_PARA:
363 case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
364 case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
365 case RndStdIds::FLY_AT_PAGE:
366 {
367 // If no position attributes are coming in, we correct the position in a way
368 // such that the fly's document coordinates are preserved.
369 // If only the alignment changes in the position attributes (text::RelOrientation::FRAME
370 // vs. text::RelOrientation::PRTAREA), we also correct the position.
371 if( SfxItemState::SET != rSet.GetItemState( RES_HORI_ORIENT, false, &pItem ))
372 pItem = nullptr;
373
374 SwFormatHoriOrient aOldH( rFormat.GetHoriOrient() );
375
376 if( text::HoriOrientation::NONE == aOldH.GetHoriOrient() && ( !pItem ||
377 aOldH.GetPos() == static_cast<const SwFormatHoriOrient*>(pItem)->GetPos() ))
378 {
379 SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldH.GetPos();
380 nPos += aOldAnchorPos.getX() - aNewAnchorPos.getX();
381
382 if( pItem )
383 {
384 SwFormatHoriOrient* pH = const_cast<SwFormatHoriOrient*>(static_cast<const SwFormatHoriOrient*>(pItem));
385 aOldH.SetHoriOrient( pH->GetHoriOrient() );
386 aOldH.SetRelationOrient( pH->GetRelationOrient() );
387 }
388 aOldH.SetPos( nPos );
389 rSet.Put( aOldH );
390 }
391
392 if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false, &pItem ))
393 pItem = nullptr;
394 SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
395
396 // #i28922# - correction: compare <aOldV.GetVertOrient() with
397 // <text::VertOrientation::NONE>
398 if( text::VertOrientation::NONE == aOldV.GetVertOrient() && (!pItem ||
399 aOldV.GetPos() == static_cast<const SwFormatVertOrient*>(pItem)->GetPos() ) )
400 {
401 SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldV.GetPos();
402 nPos += aOldAnchorPos.getY() - aNewAnchorPos.getY();
403 if( pItem )
404 {
405 SwFormatVertOrient* pV = const_cast<SwFormatVertOrient*>(static_cast<const SwFormatVertOrient*>(pItem));
406 aOldV.SetVertOrient( pV->GetVertOrient() );
407 aOldV.SetRelationOrient( pV->GetRelationOrient() );
408 }
409 aOldV.SetPos( nPos );
410 rSet.Put( aOldV );
411 }
412 }
413 break;
414 default:
415 break;
416 }
417
418 if( bNewFrames )
419 rFormat.MakeFrames();
420
421 return MAKEFRMS;
422 }
423
424 static bool
lcl_SetFlyFrameAttr(SwDoc & rDoc,sal_Int8 (SwDoc::* pSetFlyFrameAnchor)(SwFrameFormat &,SfxItemSet &,bool),SwFrameFormat & rFlyFormat,SfxItemSet & rSet)425 lcl_SetFlyFrameAttr(SwDoc & rDoc,
426 sal_Int8 (SwDoc::*pSetFlyFrameAnchor)(SwFrameFormat &, SfxItemSet &, bool),
427 SwFrameFormat & rFlyFormat, SfxItemSet & rSet)
428 {
429 // #i32968# Inserting columns in the frame causes MakeFrameFormat to put two
430 // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
431 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
432
433 // Is the anchor attribute included?
434 // If so, we pass it to a special method, which returns true
435 // if the Fly needs to be created anew, because we e.g change the FlyType.
436 sal_Int8 const nMakeFrames =
437 (SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
438 ? (rDoc.*pSetFlyFrameAnchor)( rFlyFormat, rSet, false )
439 : DONTMAKEFRMS;
440
441 const SfxPoolItem* pItem;
442 SfxItemIter aIter( rSet );
443 SfxItemSet aTmpSet( rDoc.GetAttrPool(), aFrameFormatSetRange );
444 const SfxPoolItem* pItemIter = aIter.GetCurItem();
445 do {
446 switch(pItemIter->Which())
447 {
448 case RES_FILL_ORDER:
449 case RES_BREAK:
450 case RES_PAGEDESC:
451 case RES_CNTNT:
452 case RES_FOOTER:
453 OSL_FAIL( "Unknown Fly attribute." );
454 [[fallthrough]];
455 case RES_CHAIN:
456 rSet.ClearItem(pItemIter->Which());
457 break;
458 case RES_ANCHOR:
459 if( DONTMAKEFRMS != nMakeFrames )
460 break;
461 [[fallthrough]];
462 default:
463 if( !IsInvalidItem(pItemIter) && ( SfxItemState::SET !=
464 rFlyFormat.GetAttrSet().GetItemState(pItemIter->Which(), true, &pItem ) ||
465 *pItem != *pItemIter))
466 aTmpSet.Put(*pItemIter);
467 break;
468 }
469
470 pItemIter = aIter.NextItem();
471
472 } while (pItemIter && (0 != pItemIter->Which()));
473
474 if( aTmpSet.Count() )
475 rFlyFormat.SetFormatAttr( aTmpSet );
476
477 if( MAKEFRMS == nMakeFrames )
478 rFlyFormat.MakeFrames();
479
480 return aTmpSet.Count() || MAKEFRMS == nMakeFrames;
481 }
482
CheckForUniqueItemForLineFillNameOrIndex(SfxItemSet & rSet)483 void SwDoc::CheckForUniqueItemForLineFillNameOrIndex(SfxItemSet& rSet)
484 {
485 SwDrawModel* pDrawModel = getIDocumentDrawModelAccess().GetOrCreateDrawModel();
486 SfxItemIter aIter(rSet);
487
488 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
489 {
490 if (IsInvalidItem(pItem))
491 continue;
492 std::unique_ptr<SfxPoolItem> pResult;
493
494 switch(pItem->Which())
495 {
496 case XATTR_FILLBITMAP:
497 {
498 pResult = static_cast< const XFillBitmapItem* >(pItem)->checkForUniqueItem(pDrawModel);
499 break;
500 }
501 case XATTR_LINEDASH:
502 {
503 pResult = static_cast< const XLineDashItem* >(pItem)->checkForUniqueItem(pDrawModel);
504 break;
505 }
506 case XATTR_LINESTART:
507 {
508 pResult = static_cast< const XLineStartItem* >(pItem)->checkForUniqueItem(pDrawModel);
509 break;
510 }
511 case XATTR_LINEEND:
512 {
513 pResult = static_cast< const XLineEndItem* >(pItem)->checkForUniqueItem(pDrawModel);
514 break;
515 }
516 case XATTR_FILLGRADIENT:
517 {
518 pResult = static_cast< const XFillGradientItem* >(pItem)->checkForUniqueItem(pDrawModel);
519 break;
520 }
521 case XATTR_FILLFLOATTRANSPARENCE:
522 {
523 pResult = static_cast< const XFillFloatTransparenceItem* >(pItem)->checkForUniqueItem(pDrawModel);
524 break;
525 }
526 case XATTR_FILLHATCH:
527 {
528 pResult = static_cast< const XFillHatchItem* >(pItem)->checkForUniqueItem(pDrawModel);
529 break;
530 }
531 }
532
533 if(pResult)
534 {
535 rSet.Put(*pResult);
536 }
537 }
538 }
539
SetFlyFrameAttr(SwFrameFormat & rFlyFormat,SfxItemSet & rSet)540 bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, SfxItemSet& rSet )
541 {
542 if( !rSet.Count() )
543 return false;
544
545 std::unique_ptr<SwUndoFormatAttrHelper> pSaveUndo;
546
547 if (GetIDocumentUndoRedo().DoesUndo())
548 {
549 GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
550 pSaveUndo.reset( new SwUndoFormatAttrHelper( rFlyFormat ) );
551 }
552
553 bool const bRet = lcl_SetFlyFrameAttr(*this, &SwDoc::SetFlyFrameAnchor, rFlyFormat, rSet);
554
555 if (pSaveUndo && pSaveUndo->GetUndo() )
556 {
557 GetIDocumentUndoRedo().AppendUndo( pSaveUndo->ReleaseUndo() );
558 }
559
560 getIDocumentState().SetModified();
561
562 SwTextBoxHelper::syncFlyFrameAttr(rFlyFormat, rSet);
563
564 return bRet;
565 }
566
567 // #i73249#
SetFlyFrameTitle(SwFlyFrameFormat & rFlyFrameFormat,const OUString & sNewTitle)568 void SwDoc::SetFlyFrameTitle( SwFlyFrameFormat& rFlyFrameFormat,
569 const OUString& sNewTitle )
570 {
571 if ( rFlyFrameFormat.GetObjTitle() == sNewTitle )
572 {
573 return;
574 }
575
576 ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
577
578 if (GetIDocumentUndoRedo().DoesUndo())
579 {
580 GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
581 SwUndoId::FLYFRMFMT_TITLE,
582 rFlyFrameFormat.GetObjTitle(),
583 sNewTitle ) );
584 }
585
586 rFlyFrameFormat.SetObjTitle( sNewTitle, true );
587
588 getIDocumentState().SetModified();
589 }
590
SetFlyFrameDescription(SwFlyFrameFormat & rFlyFrameFormat,const OUString & sNewDescription)591 void SwDoc::SetFlyFrameDescription( SwFlyFrameFormat& rFlyFrameFormat,
592 const OUString& sNewDescription )
593 {
594 if ( rFlyFrameFormat.GetObjDescription() == sNewDescription )
595 {
596 return;
597 }
598
599 ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
600
601 if (GetIDocumentUndoRedo().DoesUndo())
602 {
603 GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
604 SwUndoId::FLYFRMFMT_DESCRIPTION,
605 rFlyFrameFormat.GetObjDescription(),
606 sNewDescription ) );
607 }
608
609 rFlyFrameFormat.SetObjDescription( sNewDescription, true );
610
611 getIDocumentState().SetModified();
612 }
613
SetFrameFormatToFly(SwFrameFormat & rFormat,SwFrameFormat & rNewFormat,SfxItemSet * pSet,bool bKeepOrient)614 bool SwDoc::SetFrameFormatToFly( SwFrameFormat& rFormat, SwFrameFormat& rNewFormat,
615 SfxItemSet* pSet, bool bKeepOrient )
616 {
617 bool bChgAnchor = false, bFrameSz = false;
618
619 const SwFormatFrameSize aFrameSz( rFormat.GetFrameSize() );
620
621 SwUndoSetFlyFormat* pUndo = nullptr;
622 bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
623 if (bUndo)
624 {
625 pUndo = new SwUndoSetFlyFormat( rFormat, rNewFormat );
626 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
627 }
628
629 // #i32968# Inserting columns in the section causes MakeFrameFormat to put
630 // 2 objects of type SwUndoFrameFormat on the undo stack. We don't want them.
631 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
632
633 // Set the column first, or we'll have trouble with
634 //Set/Reset/Synch. and so on
635 const SfxPoolItem* pItem;
636 if( SfxItemState::SET != rNewFormat.GetAttrSet().GetItemState( RES_COL ))
637 rFormat.ResetFormatAttr( RES_COL );
638
639 if( rFormat.DerivedFrom() != &rNewFormat )
640 {
641 rFormat.SetDerivedFrom( &rNewFormat );
642
643 // 1. If not automatic = ignore; else = dispose
644 // 2. Dispose of it!
645 if( SfxItemState::SET == rNewFormat.GetAttrSet().GetItemState( RES_FRM_SIZE, false ))
646 {
647 rFormat.ResetFormatAttr( RES_FRM_SIZE );
648 bFrameSz = true;
649 }
650
651 const SfxItemSet* pAsk = pSet;
652 if( !pAsk ) pAsk = &rNewFormat.GetAttrSet();
653 if( SfxItemState::SET == pAsk->GetItemState( RES_ANCHOR, false, &pItem )
654 && static_cast<const SwFormatAnchor*>(pItem)->GetAnchorId() !=
655 rFormat.GetAnchor().GetAnchorId() )
656 {
657 if( pSet )
658 bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, *pSet, false );
659 else
660 {
661 // Needs to have the FlyFormat range, because we set attributes in it,
662 // in SetFlyFrameAnchor.
663 SfxItemSet aFlySet( *rNewFormat.GetAttrSet().GetPool(),
664 rNewFormat.GetAttrSet().GetRanges() );
665 aFlySet.Put( *pItem );
666 bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, aFlySet, false);
667 }
668 }
669 }
670
671 // Only reset vertical and horizontal orientation, if we have automatic alignment
672 // set in the template. Otherwise use the old value.
673 // If we update the frame template the Fly should NOT lose its orientation (which
674 // is not being updated!).
675 // text::HoriOrientation::NONE and text::VertOrientation::NONE are allowed now
676 if (!bKeepOrient)
677 {
678 rFormat.ResetFormatAttr(RES_VERT_ORIENT);
679 rFormat.ResetFormatAttr(RES_HORI_ORIENT);
680 }
681
682 rFormat.ResetFormatAttr( RES_PRINT, RES_SURROUND );
683 rFormat.ResetFormatAttr( RES_LR_SPACE, RES_UL_SPACE );
684 rFormat.ResetFormatAttr( RES_BACKGROUND, RES_COL );
685 rFormat.ResetFormatAttr( RES_URL, RES_EDIT_IN_READONLY );
686
687 if( !bFrameSz )
688 rFormat.SetFormatAttr( aFrameSz );
689
690 if( bChgAnchor )
691 rFormat.MakeFrames();
692
693 if( pUndo )
694 pUndo->EndListeningAll();
695
696 getIDocumentState().SetModified();
697
698 return bChgAnchor;
699 }
700
GetGrfNms(const SwFlyFrameFormat & rFormat,OUString * pGrfName,OUString * pFltName)701 void SwDoc::GetGrfNms( const SwFlyFrameFormat& rFormat, OUString* pGrfName,
702 OUString* pFltName )
703 {
704 SwNodeIndex aIdx( *rFormat.GetContent().GetContentIdx(), 1 );
705 const SwGrfNode* pGrfNd = aIdx.GetNode().GetGrfNode();
706 if( pGrfNd && pGrfNd->IsLinkedFile() )
707 pGrfNd->GetFileFilterNms( pGrfName, pFltName );
708 }
709
ChgAnchor(const SdrMarkList & _rMrkList,RndStdIds _eAnchorType,const bool _bSameOnly,const bool _bPosCorr)710 bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
711 RndStdIds _eAnchorType,
712 const bool _bSameOnly,
713 const bool _bPosCorr )
714 {
715 OSL_ENSURE( getIDocumentLayoutAccess().GetCurrentLayout(), "No layout!" );
716
717 if ( !_rMrkList.GetMarkCount() ||
718 _rMrkList.GetMark( 0 )->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject() )
719 {
720 return false;
721 }
722
723 GetIDocumentUndoRedo().StartUndo( SwUndoId::INSATTR, nullptr );
724
725 bool bUnmark = false;
726 for ( size_t i = 0; i < _rMrkList.GetMarkCount(); ++i )
727 {
728 SdrObject* pObj = _rMrkList.GetMark( i )->GetMarkedSdrObj();
729 if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
730 {
731 SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
732
733 // consider, that drawing object has
734 // no user call. E.g.: a 'virtual' drawing object is disconnected by
735 // the anchor type change of the 'master' drawing object.
736 // Continue with next selected object and assert, if this isn't excepted.
737 if ( !pContact )
738 {
739 #if OSL_DEBUG_LEVEL > 0
740 bool bNoUserCallExcepted =
741 dynamic_cast<const SwDrawVirtObj*>( pObj) != nullptr &&
742 !static_cast<SwDrawVirtObj*>(pObj)->IsConnected();
743 OSL_ENSURE( bNoUserCallExcepted, "SwDoc::ChgAnchor(..) - no contact at selected drawing object" );
744 #endif
745 continue;
746 }
747
748 // #i26791#
749 const SwFrame* pOldAnchorFrame = pContact->GetAnchorFrame( pObj );
750 const SwFrame* pNewAnchorFrame = pOldAnchorFrame;
751
752 // #i54336#
753 // Instead of only keeping the index position for an as-character
754 // anchored object the complete <SwPosition> is kept, because the
755 // anchor index position could be moved, if the object again is
756 // anchored as character.
757 std::unique_ptr<const SwPosition> xOldAsCharAnchorPos;
758 const RndStdIds eOldAnchorType = pContact->GetAnchorId();
759 if ( !_bSameOnly && eOldAnchorType == RndStdIds::FLY_AS_CHAR )
760 {
761 xOldAsCharAnchorPos.reset(new SwPosition(pContact->GetContentAnchor()));
762 }
763
764 if ( _bSameOnly )
765 _eAnchorType = eOldAnchorType;
766
767 SwFormatAnchor aNewAnch( _eAnchorType );
768 SwAnchoredObject *pAnchoredObj = pContact->GetAnchoredObj(pObj);
769 tools::Rectangle aObjRect(pAnchoredObj->GetObjRect().SVRect());
770 const Point aPt( aObjRect.TopLeft() );
771
772 switch ( _eAnchorType )
773 {
774 case RndStdIds::FLY_AT_PARA:
775 case RndStdIds::FLY_AT_CHAR:
776 {
777 const Point aNewPoint = ( pOldAnchorFrame->IsVertical() ||
778 pOldAnchorFrame->IsRightToLeft() )
779 ? aObjRect.TopRight()
780 : aPt;
781
782 // allow drawing objects in header/footer
783 pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aNewPoint );
784 if ( pNewAnchorFrame->IsTextFrame() && static_cast<const SwTextFrame*>(pNewAnchorFrame)->IsFollow() )
785 {
786 pNewAnchorFrame = static_cast<const SwTextFrame*>(pNewAnchorFrame)->FindMaster();
787 }
788 if ( pNewAnchorFrame->IsProtected() )
789 {
790 pNewAnchorFrame = nullptr;
791 }
792 else
793 {
794 SwPosition aPos( pNewAnchorFrame->IsTextFrame()
795 ? *static_cast<SwTextFrame const*>(pNewAnchorFrame)->GetTextNodeForParaProps()
796 : *static_cast<SwNoTextFrame const*>(pNewAnchorFrame)->GetNode() );
797
798 aNewAnch.SetType( _eAnchorType );
799 aNewAnch.SetAnchor( &aPos );
800 }
801 }
802 break;
803
804 case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
805 {
806 // Search the closest SwFlyFrame starting from the upper left corner.
807 SwFrame *pTextFrame;
808 {
809 SwCursorMoveState aState( MV_SETONLYTEXT );
810 SwPosition aPos( GetNodes() );
811 Point aPoint( aPt );
812 aPoint.setX(aPoint.getX() - 1);
813 getIDocumentLayoutAccess().GetCurrentLayout()->GetCursorOfst( &aPos, aPoint, &aState );
814 // consider that drawing objects can be in
815 // header/footer. Thus, <GetFrame()> by left-top-corner
816 std::pair<Point, bool> const tmp(aPt, false);
817 pTextFrame = aPos.nNode.GetNode().
818 GetContentNode()->getLayoutFrame(
819 getIDocumentLayoutAccess().GetCurrentLayout(),
820 nullptr, &tmp);
821 }
822 const SwFrame *pTmp = ::FindAnchor( pTextFrame, aPt );
823 pNewAnchorFrame = pTmp->FindFlyFrame();
824 if( pNewAnchorFrame && !pNewAnchorFrame->IsProtected() )
825 {
826 const SwFrameFormat *pTmpFormat = static_cast<const SwFlyFrame*>(pNewAnchorFrame)->GetFormat();
827 const SwFormatContent& rContent = pTmpFormat->GetContent();
828 SwPosition aPos( *rContent.GetContentIdx() );
829 aNewAnch.SetAnchor( &aPos );
830 break;
831 }
832
833 aNewAnch.SetType( RndStdIds::FLY_AT_PAGE );
834 [[fallthrough]];
835 }
836 case RndStdIds::FLY_AT_PAGE:
837 {
838 pNewAnchorFrame = getIDocumentLayoutAccess().GetCurrentLayout()->Lower();
839 while ( pNewAnchorFrame && !pNewAnchorFrame->getFrameArea().IsInside( aPt ) )
840 pNewAnchorFrame = pNewAnchorFrame->GetNext();
841 if ( !pNewAnchorFrame )
842 continue;
843
844 aNewAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnchorFrame)->GetPhyPageNum());
845 }
846 break;
847 case RndStdIds::FLY_AS_CHAR:
848 if( _bSameOnly ) // Change of position/size
849 {
850 if( !pOldAnchorFrame )
851 {
852 pContact->ConnectToLayout();
853 pOldAnchorFrame = pContact->GetAnchorFrame();
854 }
855 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pOldAnchorFrame))->Prepare();
856 }
857 else // Change of anchors
858 {
859 // allow drawing objects in header/footer
860 pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aPt );
861 if( pNewAnchorFrame->IsProtected() )
862 {
863 pNewAnchorFrame = nullptr;
864 break;
865 }
866
867 bUnmark = ( 0 != i );
868 Point aPoint( aPt );
869 aPoint.setX(aPoint.getX() - 1); // Do not load in the DrawObj!
870 aNewAnch.SetType( RndStdIds::FLY_AS_CHAR );
871 assert(pNewAnchorFrame->IsTextFrame()); // because AS_CHAR
872 SwTextFrame const*const pFrame(
873 static_cast<SwTextFrame const*>(pNewAnchorFrame));
874 SwPosition aPos( *pFrame->GetTextNodeForParaProps() );
875 if ( pNewAnchorFrame->getFrameArea().IsInside( aPoint ) )
876 {
877 // We need to find a TextNode, because only there we can anchor a
878 // content-bound DrawObject.
879 SwCursorMoveState aState( MV_SETONLYTEXT );
880 getIDocumentLayoutAccess().GetCurrentLayout()->GetCursorOfst( &aPos, aPoint, &aState );
881 }
882 else
883 {
884 if ( pNewAnchorFrame->getFrameArea().Bottom() < aPt.Y() )
885 {
886 aPos = pFrame->MapViewToModelPos(TextFrameIndex(0));
887 }
888 else
889 {
890 aPos = pFrame->MapViewToModelPos(
891 TextFrameIndex(pFrame->GetText().getLength()));
892 }
893 }
894 aNewAnch.SetAnchor( &aPos );
895 SetAttr( aNewAnch, *pContact->GetFormat() );
896 // #i26791# - adjust vertical positioning to 'center to
897 // baseline'
898 SetAttr( SwFormatVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ), *pContact->GetFormat() );
899 SwTextNode *pNd = aPos.nNode.GetNode().GetTextNode();
900 OSL_ENSURE( pNd, "Cursor not positioned at TextNode." );
901
902 SwFormatFlyCnt aFormat( pContact->GetFormat() );
903 pNd->InsertItem( aFormat, aPos.nContent.GetIndex(), 0 );
904 }
905 break;
906 default:
907 OSL_ENSURE( false, "unexpected AnchorId." );
908 }
909
910 if ( (RndStdIds::FLY_AS_CHAR != _eAnchorType) &&
911 pNewAnchorFrame &&
912 ( !_bSameOnly || pNewAnchorFrame != pOldAnchorFrame ) )
913 {
914 // #i26791# - Direct object positioning no longer needed. Apply
915 // of attributes (method call <SetAttr(..)>) takes care of the
916 // invalidation of the object position.
917 SetAttr( aNewAnch, *pContact->GetFormat() );
918 if ( _bPosCorr )
919 {
920 // #i33313# - consider not connected 'virtual' drawing
921 // objects
922 if ( dynamic_cast<const SwDrawVirtObj*>( pObj) != nullptr &&
923 !static_cast<SwDrawVirtObj*>(pObj)->IsConnected() )
924 {
925 SwRect aNewObjRect( aObjRect );
926 static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( nullptr ))
927 ->AdjustPositioningAttr( pNewAnchorFrame,
928 &aNewObjRect );
929 }
930 else
931 {
932 static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ))
933 ->AdjustPositioningAttr( pNewAnchorFrame );
934 }
935 }
936 }
937
938 // we have changed the anchoring attributes, and those are used to
939 // order the object in its sorted list, so update its position
940 pAnchoredObj->UpdateObjInSortedList();
941
942 // #i54336#
943 if (xOldAsCharAnchorPos)
944 {
945 if ( pNewAnchorFrame)
946 {
947 // We need to handle InContents in a special way:
948 // The TextAttribut needs to be destroyed which, unfortunately, also
949 // destroys the format. To avoid that, we disconnect the format from
950 // the attribute.
951 const sal_Int32 nIndx( xOldAsCharAnchorPos->nContent.GetIndex() );
952 SwTextNode* pTextNode( xOldAsCharAnchorPos->nNode.GetNode().GetTextNode() );
953 assert(pTextNode && "<SwDoc::ChgAnchor(..)> - missing previous anchor text node for as-character anchored object");
954 SwTextAttr * const pHint =
955 pTextNode->GetTextAttrForCharAt( nIndx, RES_TXTATR_FLYCNT );
956 assert(pHint && "Missing FlyInCnt-Hint.");
957 const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
958
959 // They are disconnected. We now have to destroy the attribute.
960 pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIndx, nIndx );
961 }
962 }
963 }
964 }
965
966 GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
967 getIDocumentState().SetModified();
968
969 return bUnmark;
970 }
971
Chainable(const SwFrameFormat & rSource,const SwFrameFormat & rDest)972 SwChainRet SwDoc::Chainable( const SwFrameFormat &rSource, const SwFrameFormat &rDest )
973 {
974 // The Source must not yet have a Follow.
975 const SwFormatChain &rOldChain = rSource.GetChain();
976 if ( rOldChain.GetNext() )
977 return SwChainRet::SOURCE_CHAINED;
978
979 // Target must not be equal to Source and we also must not have a closed chain.
980 const SwFrameFormat *pFormat = &rDest;
981 do {
982 if( pFormat == &rSource )
983 return SwChainRet::SELF;
984 pFormat = pFormat->GetChain().GetNext();
985 } while ( pFormat );
986
987 // There must not be a chaining from outside to inside or the other way around.
988 if( rDest.IsLowerOf( rSource ) || rSource .IsLowerOf( rDest ) )
989 return SwChainRet::SELF;
990
991 // The Target must not yet have a Master.
992 const SwFormatChain &rChain = rDest.GetChain();
993 if( rChain.GetPrev() )
994 return SwChainRet::IS_IN_CHAIN;
995
996 // Target must be empty.
997 const SwNodeIndex* pCntIdx = rDest.GetContent().GetContentIdx();
998 if( !pCntIdx )
999 return SwChainRet::NOT_FOUND;
1000
1001 SwNodeIndex aNxtIdx( *pCntIdx, 1 );
1002 const SwTextNode* pTextNd = aNxtIdx.GetNode().GetTextNode();
1003 if( !pTextNd )
1004 return SwChainRet::NOT_FOUND;
1005
1006 const sal_uLong nFlySttNd = pCntIdx->GetIndex();
1007 if( 2 != ( pCntIdx->GetNode().EndOfSectionIndex() - nFlySttNd ) ||
1008 pTextNd->GetText().getLength() )
1009 {
1010 return SwChainRet::NOT_EMPTY;
1011 }
1012
1013 for( auto pSpzFrameFm : *GetSpzFrameFormats() )
1014 {
1015 const SwFormatAnchor& rAnchor = pSpzFrameFm->GetAnchor();
1016 sal_uLong nTstSttNd;
1017 // #i20622# - to-frame anchored objects are allowed.
1018 if ( ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
1019 (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)) &&
1020 nullptr != rAnchor.GetContentAnchor() &&
1021 nFlySttNd <= ( nTstSttNd =
1022 rAnchor.GetContentAnchor()->nNode.GetIndex() ) &&
1023 nTstSttNd < nFlySttNd + 2 )
1024 {
1025 return SwChainRet::NOT_EMPTY;
1026 }
1027 }
1028
1029 // We also need to consider the right area.
1030 // Both Flys need to be located in the same area (Body, Header/Footer, Fly).
1031 // If the Source is not the selected frame, it's enough to find a suitable
1032 // one. e.g. if it's requested by the API.
1033
1034 // both in the same fly, header, footer or on the page?
1035 const SwFormatAnchor &rSrcAnchor = rSource.GetAnchor(),
1036 &rDstAnchor = rDest.GetAnchor();
1037 sal_uLong nEndOfExtras = GetNodes().GetEndOfExtras().GetIndex();
1038 bool bAllowed = false;
1039 if ( RndStdIds::FLY_AT_PAGE == rSrcAnchor.GetAnchorId() )
1040 {
1041 if ( (RndStdIds::FLY_AT_PAGE == rDstAnchor.GetAnchorId()) ||
1042 ( rDstAnchor.GetContentAnchor() &&
1043 rDstAnchor.GetContentAnchor()->nNode.GetIndex() > nEndOfExtras ))
1044 bAllowed = true;
1045 }
1046 else if( rSrcAnchor.GetContentAnchor() && rDstAnchor.GetContentAnchor() )
1047 {
1048 const SwNodeIndex &rSrcIdx = rSrcAnchor.GetContentAnchor()->nNode,
1049 &rDstIdx = rDstAnchor.GetContentAnchor()->nNode;
1050 const SwStartNode* pSttNd = nullptr;
1051 if( rSrcIdx == rDstIdx ||
1052 ( !pSttNd &&
1053 nullptr != ( pSttNd = rSrcIdx.GetNode().FindFlyStartNode() ) &&
1054 pSttNd == rDstIdx.GetNode().FindFlyStartNode() ) ||
1055 ( !pSttNd &&
1056 nullptr != ( pSttNd = rSrcIdx.GetNode().FindFooterStartNode() ) &&
1057 pSttNd == rDstIdx.GetNode().FindFooterStartNode() ) ||
1058 ( !pSttNd &&
1059 nullptr != ( pSttNd = rSrcIdx.GetNode().FindHeaderStartNode() ) &&
1060 pSttNd == rDstIdx.GetNode().FindHeaderStartNode() ) ||
1061 ( !pSttNd && rDstIdx.GetIndex() > nEndOfExtras &&
1062 rSrcIdx.GetIndex() > nEndOfExtras ))
1063 bAllowed = true;
1064 }
1065
1066 return bAllowed ? SwChainRet::OK : SwChainRet::WRONG_AREA;
1067 }
1068
Chain(SwFrameFormat & rSource,const SwFrameFormat & rDest)1069 SwChainRet SwDoc::Chain( SwFrameFormat &rSource, const SwFrameFormat &rDest )
1070 {
1071 SwChainRet nErr = Chainable( rSource, rDest );
1072 if ( nErr == SwChainRet::OK )
1073 {
1074 GetIDocumentUndoRedo().StartUndo( SwUndoId::CHAINE, nullptr );
1075
1076 SwFlyFrameFormat& rDestFormat = const_cast<SwFlyFrameFormat&>(static_cast<const SwFlyFrameFormat&>(rDest));
1077
1078 // Attach Follow to the Master.
1079 SwFormatChain aChain = rDestFormat.GetChain();
1080 aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
1081 SetAttr( aChain, rDestFormat );
1082
1083 SfxItemSet aSet( GetAttrPool(), svl::Items<RES_FRM_SIZE, RES_FRM_SIZE,
1084 RES_CHAIN, RES_CHAIN>{} );
1085
1086 // Attach Follow to the Master.
1087 aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
1088 SetAttr( aChain, rDestFormat );
1089
1090 // Attach Master to the Follow.
1091 // Make sure that the Master has a fixed height.
1092 aChain = rSource.GetChain();
1093 aChain.SetNext( &rDestFormat );
1094 aSet.Put( aChain );
1095
1096 SwFormatFrameSize aSize( rSource.GetFrameSize() );
1097 if ( aSize.GetHeightSizeType() != ATT_FIX_SIZE )
1098 {
1099 SwFlyFrame *pFly = SwIterator<SwFlyFrame,SwFormat>( rSource ).First();
1100 if ( pFly )
1101 aSize.SetHeight( pFly->getFrameArea().Height() );
1102 aSize.SetHeightSizeType( ATT_FIX_SIZE );
1103 aSet.Put( aSize );
1104 }
1105 SetAttr( aSet, rSource );
1106
1107 GetIDocumentUndoRedo().EndUndo( SwUndoId::CHAINE, nullptr );
1108 }
1109 return nErr;
1110 }
1111
Unchain(SwFrameFormat & rFormat)1112 void SwDoc::Unchain( SwFrameFormat &rFormat )
1113 {
1114 SwFormatChain aChain( rFormat.GetChain() );
1115 if ( aChain.GetNext() )
1116 {
1117 GetIDocumentUndoRedo().StartUndo( SwUndoId::UNCHAIN, nullptr );
1118 SwFrameFormat *pFollow = aChain.GetNext();
1119 aChain.SetNext( nullptr );
1120 SetAttr( aChain, rFormat );
1121 aChain = pFollow->GetChain();
1122 aChain.SetPrev( nullptr );
1123 SetAttr( aChain, *pFollow );
1124 GetIDocumentUndoRedo().EndUndo( SwUndoId::UNCHAIN, nullptr );
1125 }
1126 }
1127
1128 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1129