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