android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / docfly.cxx
bloba0edbf934d9b29362e2efb87f43ddea5f342855c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
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 #include <formatflysplit.hxx>
67 using namespace ::com::sun::star;
69 size_t SwDoc::GetFlyCount( FlyCntType eType, bool bIgnoreTextBoxes ) const
71 size_t nCount = 0;
72 const SwNodeIndex* pIdx;
74 for(sw::SpzFrameFormat* pFlyFormat: *GetSpzFrameFormats())
76 if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
77 continue;
79 if( RES_FLYFRMFMT != pFlyFormat->Which() )
80 continue;
81 pIdx = pFlyFormat->GetContent().GetContentIdx();
82 if( pIdx && pIdx->GetNodes().IsDocNodes() )
84 const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
86 switch( eType )
88 case FLYCNTTYPE_FRM:
89 if(!pNd->IsNoTextNode())
90 nCount++;
91 break;
93 case FLYCNTTYPE_GRF:
94 if( pNd->IsGrfNode() )
95 nCount++;
96 break;
98 case FLYCNTTYPE_OLE:
99 if(pNd->IsOLENode())
100 nCount++;
101 break;
103 default:
104 nCount++;
108 return nCount;
111 /// @attention If you change this, also update SwXFrameEnumeration in unocoll.
112 SwFrameFormat* SwDoc::GetFlyNum( size_t nIdx, FlyCntType eType, bool bIgnoreTextBoxes )
114 SwFrameFormat* pRetFormat = nullptr;
115 const SwNodeIndex* pIdx;
116 size_t nCount = 0;
118 for(sw::SpzFrameFormat* pFlyFormat: *GetSpzFrameFormats())
120 if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
121 continue;
123 if( RES_FLYFRMFMT != pFlyFormat->Which() )
124 continue;
125 pIdx = pFlyFormat->GetContent().GetContentIdx();
126 if( pIdx && pIdx->GetNodes().IsDocNodes() )
128 const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
129 switch( eType )
131 case FLYCNTTYPE_FRM:
132 if( !pNd->IsNoTextNode() && nIdx == nCount++)
133 pRetFormat = pFlyFormat;
134 break;
135 case FLYCNTTYPE_GRF:
136 if(pNd->IsGrfNode() && nIdx == nCount++ )
137 pRetFormat = pFlyFormat;
138 break;
139 case FLYCNTTYPE_OLE:
140 if(pNd->IsOLENode() && nIdx == nCount++)
141 pRetFormat = pFlyFormat;
142 break;
143 default:
144 if(nIdx == nCount++)
145 pRetFormat = pFlyFormat;
149 return pRetFormat;
152 std::vector<SwFrameFormat const*> SwDoc::GetFlyFrameFormats(
153 FlyCntType const eType, bool const bIgnoreTextBoxes)
155 std::vector<SwFrameFormat const*> ret;
156 ret.reserve(GetSpzFrameFormats()->size());
158 for(sw::SpzFrameFormat* pFlyFormat: *GetSpzFrameFormats())
160 if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
162 continue;
165 if (RES_FLYFRMFMT != pFlyFormat->Which())
167 continue;
170 SwNodeIndex const*const pIdx(pFlyFormat->GetContent().GetContentIdx());
171 if (pIdx && pIdx->GetNodes().IsDocNodes())
173 SwNode const*const pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
174 switch (eType)
176 case FLYCNTTYPE_FRM:
177 if (!pNd->IsNoTextNode())
178 ret.push_back(pFlyFormat);
179 break;
180 case FLYCNTTYPE_GRF:
181 if (pNd->IsGrfNode())
182 ret.push_back(pFlyFormat);
183 break;
184 case FLYCNTTYPE_OLE:
185 if (pNd->IsOLENode())
186 ret.push_back(pFlyFormat);
187 break;
188 default:
189 ret.push_back(pFlyFormat);
194 return ret;
197 static Point lcl_FindAnchorLayPos( SwDoc& rDoc, const SwFormatAnchor& rAnch,
198 const SwFrameFormat* pFlyFormat )
200 Point aRet;
201 if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
202 switch( rAnch.GetAnchorId() )
204 case RndStdIds::FLY_AS_CHAR:
205 if( pFlyFormat && rAnch.GetAnchorNode() )
207 const SwFrame* pOld = static_cast<const SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aRet );
208 if( pOld )
209 aRet = pOld->getFrameArea().Pos();
211 break;
213 case RndStdIds::FLY_AT_PARA:
214 case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
215 if( rAnch.GetAnchorNode() )
217 const SwContentNode* pNd = rAnch.GetAnchorNode()->GetContentNode();
218 std::pair<Point, bool> const tmp(aRet, false);
219 const SwFrame* pOld = pNd ? pNd->getLayoutFrame(rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
220 if( pOld )
221 aRet = pOld->getFrameArea().Pos();
223 break;
225 case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
226 if( rAnch.GetAnchorNode() )
228 const SwFlyFrameFormat* pFormat = static_cast<SwFlyFrameFormat*>(rAnch.GetAnchorNode()->
229 GetFlyFormat());
230 const SwFrame* pOld = pFormat ? pFormat->GetFrame( &aRet ) : nullptr;
231 if( pOld )
232 aRet = pOld->getFrameArea().Pos();
234 break;
236 case RndStdIds::FLY_AT_PAGE:
238 sal_uInt16 nPgNum = rAnch.GetPageNum();
239 const SwPageFrame *pPage = static_cast<SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower());
240 for( sal_uInt16 i = 1; (i <= nPgNum) && pPage; ++i,
241 pPage =static_cast<const SwPageFrame*>(pPage->GetNext()) )
242 if( i == nPgNum )
244 aRet = pPage->getFrameArea().Pos();
245 break;
248 break;
249 default:
250 break;
252 return aRet;
255 #define MAKEFRMS 0
256 #define IGNOREANCHOR 1
257 #define DONTMAKEFRMS 2
259 sal_Int8 SwDoc::SetFlyFrameAnchor( SwFrameFormat& rFormat, SfxItemSet& rSet, bool bNewFrames )
261 // Changing anchors is almost always allowed.
262 // Exception: Paragraph and character bound frames must not become
263 // page bound, if they are located in the header or footer.
264 const SwFormatAnchor &rOldAnch = rFormat.GetAnchor();
265 const RndStdIds nOld = rOldAnch.GetAnchorId();
267 SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) );
268 RndStdIds nNew = aNewAnch.GetAnchorId();
270 // Is the new anchor valid?
271 if( !aNewAnch.GetAnchorNode() && (RndStdIds::FLY_AT_FLY == nNew ||
272 (RndStdIds::FLY_AT_PARA == nNew) || (RndStdIds::FLY_AS_CHAR == nNew) ||
273 (RndStdIds::FLY_AT_CHAR == nNew) ))
275 return IGNOREANCHOR;
278 if( nOld == nNew )
279 return DONTMAKEFRMS;
281 Point aOldAnchorPos( ::lcl_FindAnchorLayPos( *this, rOldAnch, &rFormat ));
282 Point aNewAnchorPos( ::lcl_FindAnchorLayPos( *this, aNewAnch, nullptr ));
284 // Destroy the old Frames.
285 // The Views are hidden implicitly, so hiding them another time would be
286 // kind of a show!
287 rFormat.DelFrames();
289 if ( RndStdIds::FLY_AS_CHAR == nOld )
291 // We need to handle InContents in a special way:
292 // The TextAttribut needs to be destroyed which, unfortunately, also
293 // destroys the format. To avoid that, we disconnect the format from
294 // the attribute.
295 SwNode *pAnchorNode = rOldAnch.GetAnchorNode();
296 SwTextNode *pTextNode = pAnchorNode->GetTextNode();
297 OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
298 const sal_Int32 nIdx = rOldAnch.GetAnchorContentOffset();
299 SwTextAttr * const pHint =
300 pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
301 OSL_ENSURE( pHint && pHint->Which() == RES_TXTATR_FLYCNT,
302 "Missing FlyInCnt-Hint." );
303 OSL_ENSURE( pHint && pHint->GetFlyCnt().GetFrameFormat() == &rFormat,
304 "Wrong TextFlyCnt-Hint." );
305 if (pHint)
306 const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
308 // They are disconnected. We now have to destroy the attribute.
309 pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
312 // We can finally set the attribute. It needs to be the first one!
313 // Undo depends on it!
314 rFormat.SetFormatAttr( aNewAnch );
316 // Correct the position
317 switch( nNew )
319 case RndStdIds::FLY_AS_CHAR:
320 // If no position attributes are received, we have to make sure
321 // that no forbidden automatic alignment is left.
323 SwNode *pAnchorNode = aNewAnch.GetAnchorNode();
324 SwTextNode *pNd = pAnchorNode->GetTextNode();
325 OSL_ENSURE( pNd, "Cursor does not point to TextNode." );
327 SwFormatFlyCnt aFormat( static_cast<SwFlyFrameFormat*>(&rFormat) );
328 pNd->InsertItem( aFormat, aNewAnch.GetAnchorContentOffset(), 0 );
331 if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false ))
333 SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
334 bool bSet = true;
335 switch( aOldV.GetVertOrient() )
337 case text::VertOrientation::LINE_TOP: aOldV.SetVertOrient( text::VertOrientation::TOP ); break;
338 case text::VertOrientation::LINE_CENTER: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
339 case text::VertOrientation::LINE_BOTTOM: aOldV.SetVertOrient( text::VertOrientation::BOTTOM); break;
340 case text::VertOrientation::NONE: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
341 default:
342 bSet = false;
344 if( bSet )
345 rSet.Put( aOldV );
347 break;
349 case RndStdIds::FLY_AT_PARA:
350 case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
351 case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
352 case RndStdIds::FLY_AT_PAGE:
354 // If only the anchor type has changed (char -> para -> page) and the absolute position
355 // is unchanged even though there is a new relative orientation
356 // (likely because the old orientation was not valid for the new anchor type),
357 // then adjust the position to account for the moved anchor position.
358 const SwFormatHoriOrient* pHoriOrientItem = rSet.GetItemIfSet( RES_HORI_ORIENT, false );
360 SwFormatHoriOrient aOldH( rFormat.GetHoriOrient() );
361 bool bPutOldH(false);
363 if (text::HoriOrientation::NONE == aOldH.GetHoriOrient() && pHoriOrientItem
364 && text::HoriOrientation::NONE == pHoriOrientItem->GetHoriOrient()
365 && aOldH.GetPos() == pHoriOrientItem->GetPos())
367 SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldH.GetPos();
368 nPos += aOldAnchorPos.getX() - aNewAnchorPos.getX();
370 assert(aOldH.GetRelationOrient() != pHoriOrientItem->GetRelationOrient());
371 aOldH.SetRelationOrient(pHoriOrientItem->GetRelationOrient());
373 aOldH.SetPos( nPos );
374 bPutOldH = true;
376 if (nNew == RndStdIds::FLY_AT_PAGE)
378 sal_Int16 nRelOrient(pHoriOrientItem
379 ? pHoriOrientItem->GetRelationOrient()
380 : aOldH.GetRelationOrient());
381 if (sw::GetAtPageRelOrientation(nRelOrient, false))
383 SAL_INFO("sw.ui", "fixing horizontal RelOrientation for at-page anchor");
384 aOldH.SetRelationOrient(nRelOrient);
385 bPutOldH = true;
388 if (bPutOldH)
390 rSet.Put( aOldH );
393 const SwFormatVertOrient* pVertOrientItem = rSet.GetItemIfSet( RES_VERT_ORIENT, false );
394 SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
396 if (text::VertOrientation::NONE == aOldV.GetVertOrient() && pVertOrientItem
397 && text::VertOrientation::NONE == pVertOrientItem->GetVertOrient()
398 && aOldV.GetPos() == pVertOrientItem->GetPos())
400 SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldV.GetPos();
401 nPos += aOldAnchorPos.getY() - aNewAnchorPos.getY();
403 assert(aOldV.GetRelationOrient() != pVertOrientItem->GetRelationOrient());
404 aOldV.SetRelationOrient(pVertOrientItem->GetRelationOrient());
406 aOldV.SetPos( nPos );
407 rSet.Put( aOldV );
410 break;
411 default:
412 break;
415 if( bNewFrames )
416 rFormat.MakeFrames();
418 return MAKEFRMS;
421 static bool
422 lcl_SetFlyFrameAttr(SwDoc & rDoc,
423 sal_Int8 (SwDoc::*pSetFlyFrameAnchor)(SwFrameFormat &, SfxItemSet &, bool),
424 SwFrameFormat & rFlyFormat, SfxItemSet & rSet)
426 // #i32968# Inserting columns in the frame causes MakeFrameFormat to put two
427 // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
428 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
430 // Is the anchor attribute included?
431 // If so, we pass it to a special method, which returns true
432 // if the Fly needs to be created anew, because we e.g change the FlyType.
433 sal_Int8 const nMakeFrames =
434 (SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
435 ? (rDoc.*pSetFlyFrameAnchor)( rFlyFormat, rSet, false )
436 : DONTMAKEFRMS;
438 const SfxPoolItem* pItem;
439 SfxItemIter aIter( rSet );
440 SfxItemSet aTmpSet( rDoc.GetAttrPool(), aFrameFormatSetRange );
441 const SfxPoolItem* pItemIter = aIter.GetCurItem();
442 do {
443 switch(pItemIter->Which())
445 case RES_FILL_ORDER:
446 case RES_BREAK:
447 case RES_PAGEDESC:
448 case RES_CNTNT:
449 case RES_FOOTER:
450 OSL_FAIL( "Unknown Fly attribute." );
451 [[fallthrough]];
452 case RES_CHAIN:
453 rSet.ClearItem(pItemIter->Which());
454 break;
455 case RES_ANCHOR:
456 if( DONTMAKEFRMS != nMakeFrames )
457 break;
458 [[fallthrough]];
459 default:
460 if( !IsInvalidItem(pItemIter) && ( SfxItemState::SET !=
461 rFlyFormat.GetAttrSet().GetItemState(pItemIter->Which(), true, &pItem ) ||
462 *pItem != *pItemIter))
463 aTmpSet.Put(*pItemIter);
464 break;
467 pItemIter = aIter.NextItem();
469 } while (pItemIter && (0 != pItemIter->Which()));
471 if( aTmpSet.Count() )
472 rFlyFormat.SetFormatAttr( aTmpSet );
474 if( MAKEFRMS == nMakeFrames )
475 rFlyFormat.MakeFrames();
477 return aTmpSet.Count() || MAKEFRMS == nMakeFrames;
480 void SwDoc::CheckForUniqueItemForLineFillNameOrIndex(SfxItemSet& rSet)
482 SwDrawModel* pDrawModel = getIDocumentDrawModelAccess().GetOrCreateDrawModel();
483 SfxItemIter aIter(rSet);
485 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
487 if (IsInvalidItem(pItem))
488 continue;
489 std::unique_ptr<SfxPoolItem> pResult;
491 switch(pItem->Which())
493 case XATTR_FILLBITMAP:
495 pResult = pItem->StaticWhichCast(XATTR_FILLBITMAP).checkForUniqueItem(pDrawModel);
496 break;
498 case XATTR_LINEDASH:
500 pResult = pItem->StaticWhichCast(XATTR_LINEDASH).checkForUniqueItem(pDrawModel);
501 break;
503 case XATTR_LINESTART:
505 pResult = pItem->StaticWhichCast(XATTR_LINESTART).checkForUniqueItem(pDrawModel);
506 break;
508 case XATTR_LINEEND:
510 pResult = pItem->StaticWhichCast(XATTR_LINEEND).checkForUniqueItem(pDrawModel);
511 break;
513 case XATTR_FILLGRADIENT:
515 pResult = pItem->StaticWhichCast(XATTR_FILLGRADIENT).checkForUniqueItem(pDrawModel);
516 break;
518 case XATTR_FILLFLOATTRANSPARENCE:
520 pResult = pItem->StaticWhichCast(XATTR_FILLFLOATTRANSPARENCE).checkForUniqueItem(pDrawModel);
521 break;
523 case XATTR_FILLHATCH:
525 pResult = pItem->StaticWhichCast(XATTR_FILLHATCH).checkForUniqueItem(pDrawModel);
526 break;
530 if(pResult)
532 rSet.Put(std::move(pResult));
537 bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, SfxItemSet& rSet )
539 if( !rSet.Count() )
540 return false;
542 SwDocModifyAndUndoGuard guard(rFlyFormat);
544 bool const bRet = lcl_SetFlyFrameAttr(*this, &SwDoc::SetFlyFrameAnchor, rFlyFormat, rSet);
546 //SwTextBoxHelper::syncFlyFrameAttr(rFlyFormat, rSet);
548 return bRet;
551 // #i73249#
552 void SwDoc::SetFlyFrameTitle( SwFlyFrameFormat& rFlyFrameFormat,
553 const OUString& sNewTitle )
555 if ( rFlyFrameFormat.GetObjTitle() == sNewTitle )
557 return;
560 ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
562 if (GetIDocumentUndoRedo().DoesUndo())
564 GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
565 SwUndoId::FLYFRMFMT_TITLE,
566 rFlyFrameFormat.GetObjTitle(),
567 sNewTitle ) );
570 rFlyFrameFormat.SetObjTitle( sNewTitle, true );
572 getIDocumentState().SetModified();
575 void SwDoc::SetFlyFrameDescription( SwFlyFrameFormat& rFlyFrameFormat,
576 const OUString& sNewDescription )
578 if ( rFlyFrameFormat.GetObjDescription() == sNewDescription )
580 return;
583 ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
585 if (GetIDocumentUndoRedo().DoesUndo())
587 GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
588 SwUndoId::FLYFRMFMT_DESCRIPTION,
589 rFlyFrameFormat.GetObjDescription(),
590 sNewDescription ) );
593 rFlyFrameFormat.SetObjDescription( sNewDescription, true );
595 getIDocumentState().SetModified();
598 void SwDoc::SetFlyFrameDecorative(SwFlyFrameFormat& rFlyFrameFormat,
599 bool const isDecorative)
601 if (rFlyFrameFormat.GetAttrSet().Get(RES_DECORATIVE).GetValue() == isDecorative)
603 return;
606 ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
608 if (GetIDocumentUndoRedo().DoesUndo())
610 GetIDocumentUndoRedo().AppendUndo(
611 std::make_unique<SwUndoFlyDecorative>(rFlyFrameFormat, isDecorative));
614 rFlyFrameFormat.SetObjDecorative(isDecorative);
616 getIDocumentState().SetModified();
620 bool SwDoc::SetFrameFormatToFly( SwFrameFormat& rFormat, SwFrameFormat& rNewFormat,
621 SfxItemSet* pSet, bool bKeepOrient )
623 bool bChgAnchor = false, bFrameSz = false;
625 const SwFormatFrameSize aFrameSz( rFormat.GetFrameSize() );
627 SwUndoSetFlyFormat* pUndo = nullptr;
628 bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
629 if (bUndo)
631 pUndo = new SwUndoSetFlyFormat( rFormat, rNewFormat );
632 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
635 // #i32968# Inserting columns in the section causes MakeFrameFormat to put
636 // 2 objects of type SwUndoFrameFormat on the undo stack. We don't want them.
637 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
639 // Set the column first, or we'll have trouble with
640 //Set/Reset/Synch. and so on
641 if( SfxItemState::SET != rNewFormat.GetAttrSet().GetItemState( RES_COL ))
642 rFormat.ResetFormatAttr( RES_COL );
644 if( rFormat.DerivedFrom() != &rNewFormat )
646 rFormat.SetDerivedFrom( &rNewFormat );
648 // 1. If not automatic = ignore; else = dispose
649 // 2. Dispose of it!
650 if( SfxItemState::SET == rNewFormat.GetAttrSet().GetItemState( RES_FRM_SIZE, false ))
652 rFormat.ResetFormatAttr( RES_FRM_SIZE );
653 bFrameSz = true;
656 const SfxItemSet* pAsk = pSet;
657 if( !pAsk ) pAsk = &rNewFormat.GetAttrSet();
658 const SwFormatAnchor* pFormatAnchor = pAsk->GetItemIfSet( RES_ANCHOR, false );
659 if( pFormatAnchor
660 && pFormatAnchor->GetAnchorId() !=
661 rFormat.GetAnchor().GetAnchorId() )
663 if( pSet )
664 bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, *pSet, false );
665 else
667 // Needs to have the FlyFormat range, because we set attributes in it,
668 // in SetFlyFrameAnchor.
669 SfxItemSet aFlySet( *rNewFormat.GetAttrSet().GetPool(),
670 rNewFormat.GetAttrSet().GetRanges() );
671 aFlySet.Put( *pFormatAnchor );
672 bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, aFlySet, false);
677 // Only reset vertical and horizontal orientation, if we have automatic alignment
678 // set in the template. Otherwise use the old value.
679 // If we update the frame template the Fly should NOT lose its orientation (which
680 // is not being updated!).
681 // text::HoriOrientation::NONE and text::VertOrientation::NONE are allowed now
682 if (!bKeepOrient)
684 rFormat.ResetFormatAttr(RES_VERT_ORIENT);
685 rFormat.ResetFormatAttr(RES_HORI_ORIENT);
688 rFormat.ResetFormatAttr( RES_PRINT, RES_SURROUND );
689 rFormat.ResetFormatAttr( RES_LR_SPACE, RES_UL_SPACE );
690 rFormat.ResetFormatAttr( RES_BACKGROUND, RES_COL );
691 rFormat.ResetFormatAttr( RES_EDIT_IN_READONLY );
693 if( !bFrameSz )
694 rFormat.SetFormatAttr( aFrameSz );
696 if( bChgAnchor )
697 rFormat.MakeFrames();
699 if( pUndo )
700 pUndo->EndListeningAll();
702 getIDocumentState().SetModified();
704 return bChgAnchor;
707 void SwDoc::GetGrfNms( const SwFlyFrameFormat& rFormat, OUString* pGrfName,
708 OUString* pFltName )
710 SwNodeIndex aIdx( *rFormat.GetContent().GetContentIdx(), 1 );
711 const SwGrfNode* pGrfNd = aIdx.GetNode().GetGrfNode();
712 if( pGrfNd && pGrfNd->IsLinkedFile() )
713 pGrfNd->GetFileFilterNms( pGrfName, pFltName );
716 bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
717 RndStdIds _eAnchorType,
718 const bool _bSameOnly,
719 const bool _bPosCorr )
721 OSL_ENSURE( getIDocumentLayoutAccess().GetCurrentLayout(), "No layout!" );
723 if ( !_rMrkList.GetMarkCount() ||
724 _rMrkList.GetMark( 0 )->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject() )
726 return false;
729 GetIDocumentUndoRedo().StartUndo( SwUndoId::INSATTR, nullptr );
731 bool bUnmark = false;
732 for ( size_t i = 0; i < _rMrkList.GetMarkCount(); ++i )
734 SdrObject* pObj = _rMrkList.GetMark( i )->GetMarkedSdrObj();
735 if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
737 SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
739 // consider, that drawing object has
740 // no user call. E.g.: a 'virtual' drawing object is disconnected by
741 // the anchor type change of the 'master' drawing object.
742 // Continue with next selected object and assert, if this isn't excepted.
743 if ( !pContact )
745 #if OSL_DEBUG_LEVEL > 0
746 auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj);
747 bool bNoUserCallExcepted = pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected();
748 OSL_ENSURE( bNoUserCallExcepted, "SwDoc::ChgAnchor(..) - no contact at selected drawing object" );
749 #endif
750 continue;
753 // #i26791#
754 const SwFrame* pOldAnchorFrame = pContact->GetAnchorFrame( pObj );
755 const SwFrame* pNewAnchorFrame = pOldAnchorFrame;
757 // #i54336#
758 // Instead of only keeping the index position for an as-character
759 // anchored object the complete <SwPosition> is kept, because the
760 // anchor index position could be moved, if the object again is
761 // anchored as character.
762 std::optional<SwPosition> oOldAsCharAnchorPos;
763 const RndStdIds eOldAnchorType = pContact->GetAnchorId();
764 if ( !_bSameOnly && eOldAnchorType == RndStdIds::FLY_AS_CHAR )
766 oOldAsCharAnchorPos.emplace(*pContact->GetAnchorFormat().GetContentAnchor());
769 if ( _bSameOnly )
770 _eAnchorType = eOldAnchorType;
772 SwFormatAnchor aNewAnch( _eAnchorType );
773 SwAnchoredObject *pAnchoredObj = pContact->GetAnchoredObj(pObj);
774 tools::Rectangle aObjRect(pAnchoredObj->GetObjRect().SVRect());
775 const Point aPt( aObjRect.TopLeft() );
777 switch ( _eAnchorType )
779 case RndStdIds::FLY_AT_PARA:
780 case RndStdIds::FLY_AT_CHAR:
782 const Point aNewPoint = ( pOldAnchorFrame->IsVertical() ||
783 pOldAnchorFrame->IsRightToLeft() )
784 ? aObjRect.TopRight()
785 : aPt;
787 // allow drawing objects in header/footer
788 pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aNewPoint );
789 if ( pNewAnchorFrame->IsTextFrame() && static_cast<const SwTextFrame*>(pNewAnchorFrame)->IsFollow() )
791 pNewAnchorFrame = static_cast<const SwTextFrame*>(pNewAnchorFrame)->FindMaster();
793 if ( pNewAnchorFrame->IsProtected() )
795 pNewAnchorFrame = nullptr;
797 else
799 SwPosition aPos( pNewAnchorFrame->IsTextFrame()
800 ? *static_cast<SwTextFrame const*>(pNewAnchorFrame)->GetTextNodeForParaProps()
801 : *static_cast<SwNoTextFrame const*>(pNewAnchorFrame)->GetNode() );
803 aNewAnch.SetType( _eAnchorType );
804 aNewAnch.SetAnchor( &aPos );
807 break;
809 case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
811 // Search the closest SwFlyFrame starting from the upper left corner.
812 SwFrame *pTextFrame;
814 SwCursorMoveState aState( CursorMoveState::SetOnlyText );
815 SwPosition aPos( GetNodes() );
816 Point aPoint( aPt );
817 aPoint.setX(aPoint.getX() - 1);
818 getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
819 // consider that drawing objects can be in
820 // header/footer. Thus, <GetFrame()> by left-top-corner
821 std::pair<Point, bool> const tmp(aPt, false);
822 pTextFrame = aPos.GetNode().
823 GetContentNode()->getLayoutFrame(
824 getIDocumentLayoutAccess().GetCurrentLayout(),
825 nullptr, &tmp);
827 const SwFrame *pTmp = ::FindAnchor( pTextFrame, aPt );
828 pNewAnchorFrame = pTmp->FindFlyFrame();
829 if( pNewAnchorFrame && !pNewAnchorFrame->IsProtected() )
831 const SwFrameFormat *pTmpFormat = static_cast<const SwFlyFrame*>(pNewAnchorFrame)->GetFormat();
832 const SwFormatContent& rContent = pTmpFormat->GetContent();
833 SwPosition aPos( *rContent.GetContentIdx() );
834 aNewAnch.SetAnchor( &aPos );
835 break;
838 aNewAnch.SetType( RndStdIds::FLY_AT_PAGE );
839 [[fallthrough]];
841 case RndStdIds::FLY_AT_PAGE:
843 pNewAnchorFrame = getIDocumentLayoutAccess().GetCurrentLayout()->Lower();
844 while ( pNewAnchorFrame && !pNewAnchorFrame->getFrameArea().Contains( aPt ) )
845 pNewAnchorFrame = pNewAnchorFrame->GetNext();
846 if ( !pNewAnchorFrame )
847 continue;
849 aNewAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnchorFrame)->GetPhyPageNum());
851 break;
852 case RndStdIds::FLY_AS_CHAR:
853 if( _bSameOnly ) // Change of position/size
855 if( !pOldAnchorFrame )
857 pContact->ConnectToLayout();
858 pOldAnchorFrame = pContact->GetAnchorFrame();
860 const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pOldAnchorFrame))->Prepare();
862 else // Change of anchors
864 // allow drawing objects in header/footer
865 pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aPt );
866 if( pNewAnchorFrame->IsProtected() )
868 pNewAnchorFrame = nullptr;
869 break;
872 bUnmark = ( 0 != i );
873 Point aPoint( aPt );
874 aPoint.setX(aPoint.getX() - 1); // Do not load in the DrawObj!
875 aNewAnch.SetType( RndStdIds::FLY_AS_CHAR );
876 assert(pNewAnchorFrame->IsTextFrame()); // because AS_CHAR
877 SwTextFrame const*const pFrame(
878 static_cast<SwTextFrame const*>(pNewAnchorFrame));
879 SwPosition aPos( *pFrame->GetTextNodeForParaProps() );
880 if ( pNewAnchorFrame->getFrameArea().Contains( aPoint ) )
882 // We need to find a TextNode, because only there we can anchor a
883 // content-bound DrawObject.
884 SwCursorMoveState aState( CursorMoveState::SetOnlyText );
885 getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
887 else
889 if ( pNewAnchorFrame->getFrameArea().Bottom() < aPt.Y() )
891 aPos = pFrame->MapViewToModelPos(TextFrameIndex(0));
893 else
895 aPos = pFrame->MapViewToModelPos(
896 TextFrameIndex(pFrame->GetText().getLength()));
899 aNewAnch.SetAnchor( &aPos );
900 SetAttr( aNewAnch, *pContact->GetFormat() );
901 // #i26791# - adjust vertical positioning to 'center to
902 // baseline'
903 SetAttr( SwFormatVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ), *pContact->GetFormat() );
904 SwTextNode *pNd = aPos.GetNode().GetTextNode();
905 OSL_ENSURE( pNd, "Cursor not positioned at TextNode." );
907 SwFormatFlyCnt aFormat( pContact->GetFormat() );
908 pNd->InsertItem( aFormat, aPos.GetContentIndex(), 0 );
910 // Has a textbox attached to the format? Sync it as well!
911 if (pContact->GetFormat() && pContact->GetFormat()->GetOtherTextBoxFormats())
913 SwTextBoxHelper::synchronizeGroupTextBoxProperty(
914 SwTextBoxHelper::changeAnchor, pContact->GetFormat(), pObj);
917 break;
918 default:
919 OSL_ENSURE( false, "unexpected AnchorId." );
922 if ( (RndStdIds::FLY_AS_CHAR != _eAnchorType) &&
923 pNewAnchorFrame &&
924 ( !_bSameOnly || pNewAnchorFrame != pOldAnchorFrame ) )
926 // #i26791# - Direct object positioning no longer needed. Apply
927 // of attributes (method call <SetAttr(..)>) takes care of the
928 // invalidation of the object position.
929 if ( _bPosCorr )
931 // #i33313# - consider not connected 'virtual' drawing
932 // objects
933 auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj);
934 if ( pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected() )
936 SwRect aNewObjRect( aObjRect );
937 static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( nullptr ))
938 ->AdjustPositioningAttr( pNewAnchorFrame,
939 &aNewObjRect );
941 else
943 static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ))
944 ->AdjustPositioningAttr( pNewAnchorFrame );
947 if (aNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
949 SwFormatHoriOrient item(pContact->GetFormat()->GetHoriOrient());
950 sal_Int16 nRelOrient(item.GetRelationOrient());
951 if (sw::GetAtPageRelOrientation(nRelOrient, false))
953 SAL_INFO("sw.ui", "fixing horizontal RelOrientation for at-page anchor");
954 item.SetRelationOrient(nRelOrient);
955 SetAttr(item, *pContact->GetFormat());
958 // tdf#136385 set the anchor last - otherwise it messes up the
959 // position in SwDrawContact::Changed_() callback
960 SetAttr(aNewAnch, *pContact->GetFormat());
963 // we have changed the anchoring attributes, and those are used to
964 // order the object in its sorted list, so update its position
965 pAnchoredObj->UpdateObjInSortedList();
967 // #i54336#
968 if (oOldAsCharAnchorPos)
970 if ( pNewAnchorFrame)
972 // We need to handle InContents in a special way:
973 // The TextAttribut needs to be destroyed which, unfortunately, also
974 // destroys the format. To avoid that, we disconnect the format from
975 // the attribute.
976 const sal_Int32 nIndx( oOldAsCharAnchorPos->GetContentIndex() );
977 SwTextNode* pTextNode( oOldAsCharAnchorPos->GetNode().GetTextNode() );
978 assert(pTextNode && "<SwDoc::ChgAnchor(..)> - missing previous anchor text node for as-character anchored object");
979 SwTextAttr * const pHint =
980 pTextNode->GetTextAttrForCharAt( nIndx, RES_TXTATR_FLYCNT );
981 assert(pHint && "Missing FlyInCnt-Hint.");
982 const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
984 // They are disconnected. We now have to destroy the attribute.
985 pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIndx, nIndx );
991 GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
992 getIDocumentState().SetModified();
994 return bUnmark;
997 SwChainRet SwDoc::Chainable( const SwFrameFormat &rSource, const SwFrameFormat &rDest )
999 // The Source must not yet have a Follow.
1000 const SwFormatChain &rOldChain = rSource.GetChain();
1001 if ( rOldChain.GetNext() )
1002 return SwChainRet::SOURCE_CHAINED;
1004 // Target must not be equal to Source and we also must not have a closed chain.
1005 const SwFrameFormat *pFormat = &rDest;
1006 do {
1007 if( pFormat == &rSource )
1008 return SwChainRet::SELF;
1009 pFormat = pFormat->GetChain().GetNext();
1010 } while ( pFormat );
1012 // There must not be a chaining from outside to inside or the other way around.
1013 if( rDest.IsLowerOf( rSource ) || rSource .IsLowerOf( rDest ) )
1014 return SwChainRet::SELF;
1016 // The Target must not yet have a Master.
1017 const SwFormatChain &rChain = rDest.GetChain();
1018 if( rChain.GetPrev() )
1019 return SwChainRet::IS_IN_CHAIN;
1021 // Split flys are incompatible with chaining.
1022 const SwFormatFlySplit& rOldSplit = rSource.GetFlySplit();
1023 if (rOldSplit.GetValue())
1025 return SwChainRet::SOURCE_CHAINED;
1027 const SwFormatFlySplit& rNewSplit = rDest.GetFlySplit();
1028 if (rNewSplit.GetValue())
1030 return SwChainRet::IS_IN_CHAIN;
1033 // Target must be empty.
1034 const SwNodeIndex* pCntIdx = rDest.GetContent().GetContentIdx();
1035 if( !pCntIdx )
1036 return SwChainRet::NOT_FOUND;
1038 SwNodeIndex aNxtIdx( *pCntIdx, 1 );
1039 const SwTextNode* pTextNd = aNxtIdx.GetNode().GetTextNode();
1040 if( !pTextNd )
1041 return SwChainRet::NOT_FOUND;
1043 const SwNodeOffset nFlySttNd = pCntIdx->GetIndex();
1044 if( SwNodeOffset(2) != ( pCntIdx->GetNode().EndOfSectionIndex() - nFlySttNd ) ||
1045 pTextNd->GetText().getLength() )
1047 return SwChainRet::NOT_EMPTY;
1050 for(sw::SpzFrameFormat* pSpzFrameFm: *GetSpzFrameFormats())
1052 const SwFormatAnchor& rAnchor = pSpzFrameFm->GetAnchor();
1053 // #i20622# - to-frame anchored objects are allowed.
1054 if ( (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA) &&
1055 (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_CHAR) )
1056 continue;
1057 if ( nullptr == rAnchor.GetAnchorNode() )
1058 continue;
1059 SwNodeOffset nTstSttNd = rAnchor.GetAnchorNode()->GetIndex();
1060 if( nFlySttNd <= nTstSttNd && nTstSttNd < nFlySttNd + SwNodeOffset(2) )
1062 return SwChainRet::NOT_EMPTY;
1066 // We also need to consider the right area.
1067 // Both Flys need to be located in the same area (Body, Header/Footer, Fly).
1068 // If the Source is not the selected frame, it's enough to find a suitable
1069 // one. e.g. if it's requested by the API.
1071 // both in the same fly, header, footer or on the page?
1072 const SwFormatAnchor &rSrcAnchor = rSource.GetAnchor(),
1073 &rDstAnchor = rDest.GetAnchor();
1074 SwNodeOffset nEndOfExtras = GetNodes().GetEndOfExtras().GetIndex();
1075 bool bAllowed = false;
1076 if ( RndStdIds::FLY_AT_PAGE == rSrcAnchor.GetAnchorId() )
1078 if ( (RndStdIds::FLY_AT_PAGE == rDstAnchor.GetAnchorId()) ||
1079 ( rDstAnchor.GetAnchorNode() &&
1080 rDstAnchor.GetAnchorNode()->GetIndex() > nEndOfExtras ))
1081 bAllowed = true;
1083 else if( rSrcAnchor.GetAnchorNode() && rDstAnchor.GetAnchorNode() )
1085 const SwNode &rSrcNd = *rSrcAnchor.GetAnchorNode(),
1086 &rDstNd = *rDstAnchor.GetAnchorNode();
1087 const SwStartNode* pSttNd = nullptr;
1088 if( rSrcNd == rDstNd ||
1089 ( !pSttNd &&
1090 nullptr != ( pSttNd = rSrcNd.FindFlyStartNode() ) &&
1091 pSttNd == rDstNd.FindFlyStartNode() ) ||
1092 ( !pSttNd &&
1093 nullptr != ( pSttNd = rSrcNd.FindFooterStartNode() ) &&
1094 pSttNd == rDstNd.FindFooterStartNode() ) ||
1095 ( !pSttNd &&
1096 nullptr != ( pSttNd = rSrcNd.FindHeaderStartNode() ) &&
1097 pSttNd == rDstNd.FindHeaderStartNode() ) ||
1098 ( !pSttNd && rDstNd.GetIndex() > nEndOfExtras &&
1099 rSrcNd.GetIndex() > nEndOfExtras ))
1100 bAllowed = true;
1103 return bAllowed ? SwChainRet::OK : SwChainRet::WRONG_AREA;
1106 SwChainRet SwDoc::Chain( SwFrameFormat &rSource, const SwFrameFormat &rDest )
1108 SwChainRet nErr = Chainable( rSource, rDest );
1109 if ( nErr == SwChainRet::OK )
1111 GetIDocumentUndoRedo().StartUndo( SwUndoId::CHAINE, nullptr );
1113 SwFlyFrameFormat& rDestFormat = const_cast<SwFlyFrameFormat&>(static_cast<const SwFlyFrameFormat&>(rDest));
1115 // Attach Follow to the Master.
1116 SwFormatChain aChain = rDestFormat.GetChain();
1117 aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
1118 SetAttr( aChain, rDestFormat );
1120 SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE,
1121 RES_CHAIN, RES_CHAIN> aSet( GetAttrPool() );
1123 // Attach Follow to the Master.
1124 aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
1125 SetAttr( aChain, rDestFormat );
1127 // Attach Master to the Follow.
1128 // Make sure that the Master has a fixed height.
1129 aChain = rSource.GetChain();
1130 aChain.SetNext( &rDestFormat );
1131 aSet.Put( aChain );
1133 SwFormatFrameSize aSize( rSource.GetFrameSize() );
1134 if ( aSize.GetHeightSizeType() != SwFrameSize::Fixed )
1136 SwFlyFrame *pFly = SwIterator<SwFlyFrame,SwFormat>( rSource ).First();
1137 if ( pFly )
1138 aSize.SetHeight( pFly->getFrameArea().Height() );
1139 aSize.SetHeightSizeType( SwFrameSize::Fixed );
1140 aSet.Put( aSize );
1142 SetAttr( aSet, rSource );
1144 GetIDocumentUndoRedo().EndUndo( SwUndoId::CHAINE, nullptr );
1146 return nErr;
1149 void SwDoc::Unchain( SwFrameFormat &rFormat )
1151 SwFormatChain aChain( rFormat.GetChain() );
1152 if ( aChain.GetNext() )
1154 GetIDocumentUndoRedo().StartUndo( SwUndoId::UNCHAIN, nullptr );
1155 SwFrameFormat *pFollow = aChain.GetNext();
1156 aChain.SetNext( nullptr );
1157 SetAttr( aChain, rFormat );
1158 aChain = pFollow->GetChain();
1159 aChain.SetPrev( nullptr );
1160 SetAttr( aChain, *pFollow );
1161 GetIDocumentUndoRedo().EndUndo( SwUndoId::UNCHAIN, nullptr );
1165 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */