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