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