Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / flycnt.cxx
blob09fd24f28c4afb8c33bb99de945f894d9b531b4d
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 <sal/log.hxx>
21 #include <osl/diagnose.h>
22 #include <svx/swframetypes.hxx>
23 #include <pagefrm.hxx>
24 #include <txtfrm.hxx>
25 #include <notxtfrm.hxx>
26 #include <doc.hxx>
27 #include <pam.hxx>
28 #include <IDocumentUndoRedo.hxx>
29 #include <IDocumentSettingAccess.hxx>
30 #include <IDocumentDrawModelAccess.hxx>
31 #include <frmtool.hxx>
32 #include <dflyobj.hxx>
33 #include <fmtanchr.hxx>
34 #include <fmtornt.hxx>
35 #include <fmtfsize.hxx>
36 #include <fmtsrnd.hxx>
37 #include <txatbase.hxx>
39 #include <tabfrm.hxx>
40 #include <flyfrms.hxx>
41 #include <crstate.hxx>
42 #include <sectfrm.hxx>
44 #include <tocntntanchoredobjectposition.hxx>
45 #include <sortedobjs.hxx>
46 #include <layouter.hxx>
47 #include "objectformattertxtfrm.hxx"
48 #include <HandleAnchorNodeChg.hxx>
49 #include <ndtxt.hxx>
50 #include <textboxhelper.hxx>
51 #include <fmtfollowtextflow.hxx>
52 #include <unoprnms.hxx>
53 #include <rootfrm.hxx>
54 #include <bodyfrm.hxx>
56 using namespace ::com::sun::star;
58 namespace
61 SwTwips lcl_GetTopForObjPos(const SwContentFrame* pCnt, const bool bVert, const bool bVertL2R)
63 if ( bVert )
65 SwTwips aResult = pCnt->getFrameArea().Left();
66 if ( bVertL2R )
67 aResult += pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
68 else
69 aResult += pCnt->getFrameArea().Width() - pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
70 return aResult;
72 else
73 return pCnt->getFrameArea().Top() + pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
78 SwFlyAtContentFrame::SwFlyAtContentFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch, bool bFollow ) :
79 SwFlyFreeFrame( pFormat, pSib, pAnch, bFollow ),
80 SwFlowFrame(static_cast<SwFrame&>(*this))
82 m_bAtCnt = true;
83 m_bAutoPosition = (RndStdIds::FLY_AT_CHAR == pFormat->GetAnchor().GetAnchorId());
86 SwFlyAtContentFrame::SwFlyAtContentFrame(SwFlyAtContentFrame& rPrecede)
87 : SwFlyAtContentFrame(rPrecede.GetFormat(), const_cast<SwFrame*>(rPrecede.GetAnchorFrame()),
88 const_cast<SwFrame*>(rPrecede.GetAnchorFrame()), /*bFollow=*/true)
90 SetFollow(rPrecede.GetFollow());
91 rPrecede.SetFollow(this);
94 SwFlyAtContentFrame::~SwFlyAtContentFrame()
98 // #i28701#
100 void SwFlyAtContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
102 if (rHint.GetId() != SfxHintId::SwLegacyModify)
104 SwFlyFrame::SwClientNotify(rMod, rHint);
105 return;
107 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
108 const SwFormatAnchor* pAnch = pLegacy->m_pNew ? GetAnchorFromPoolItem(*pLegacy->m_pNew) : nullptr;
109 if(!pAnch)
111 SwFlyFrame::SwClientNotify(rMod, rHint);
112 return;
114 OSL_ENSURE(pAnch->GetAnchorId() == GetFormat()->GetAnchor().GetAnchorId(),
115 "Illegal change of anchor type.");
117 //Unregister, get hold of a new anchor and attach it
118 SwRect aOld(GetObjRectWithSpaces());
119 SwPageFrame* pOldPage = FindPageFrame();
120 const SwFrame* pOldAnchor = GetAnchorFrame();
121 SwContentFrame* pContent = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetAnchorFrame()));
122 AnchorFrame()->RemoveFly(this);
124 const bool bBodyFootnote = (pContent->IsInDocBody() || pContent->IsInFootnote());
126 // Search the new anchor using the NodeIdx; the relation between old
127 // and new NodeIdx determines the search direction
128 const SwNodeIndex aNewIdx(*pAnch->GetAnchorNode());
129 SwNodeIndex aOldIdx(pContent->IsTextFrame()
130 // sw_redlinehide: can pick any node here, the compare with
131 // FrameContainsNode should catch it
132 ? *static_cast<SwTextFrame *>(pContent)->GetTextNodeFirst()
133 : *static_cast<SwNoTextFrame *>(pContent)->GetNode());
135 //fix: depending on which index was smaller, searching in the do-while
136 //loop previously was done forward or backwards respectively. This however
137 //could lead to an infinite loop. To at least avoid the loop, searching
138 //is now done in only one direction. Getting hold of a frame from the node
139 //is still possible if the new anchor could not be found. Chances are
140 //good that this will be the correct one.
141 // consider the case that at found anchor frame candidate already a
142 // fly frame of the given fly format is registered.
143 // consider, that <pContent> is the already
144 // the new anchor frame.
145 bool bFound(FrameContainsNode(*pContent, aNewIdx.GetIndex()));
146 const bool bNext = !bFound && aOldIdx < aNewIdx;
147 while(pContent && !bFound)
151 if(bNext)
152 pContent = pContent->GetNextContentFrame();
153 else
154 pContent = pContent->GetPrevContentFrame();
155 } while(pContent &&
156 (bBodyFootnote != (pContent->IsInDocBody() || pContent->IsInFootnote())));
157 if(pContent)
158 bFound = FrameContainsNode(*pContent, aNewIdx.GetIndex());
160 // check, if at found anchor frame candidate already a fly frame
161 // of the given fly frame format is registered.
162 if(bFound && pContent && pContent->GetDrawObjs())
164 SwFrameFormat* pMyFlyFrameFormat(&GetFrameFormat());
165 SwSortedObjs &rObjs = *pContent->GetDrawObjs();
166 for(SwAnchoredObject* rObj : rObjs)
168 SwFlyFrame* pFlyFrame = rObj->DynCastFlyFrame();
169 if (pFlyFrame &&
170 &(pFlyFrame->GetFrameFormat()) == pMyFlyFrameFormat)
172 bFound = false;
173 break;
178 if(!pContent)
180 SwContentNode *pNode = aNewIdx.GetNode().GetContentNode();
181 std::pair<Point, bool> const tmp(pOldAnchor->getFrameArea().Pos(), false);
182 pContent = pNode->getLayoutFrame(getRootFrame(), nullptr, &tmp);
183 OSL_ENSURE(pContent, "New anchor not found");
185 //Flys are never attached to a follow, but always on the master which
186 //we are going to search now.
187 SwContentFrame* pFlow = pContent;
188 while(pFlow->IsFollow())
189 pFlow = pFlow->FindMaster();
190 pContent = pFlow;
192 //and *puff* it's attached...
193 pContent->AppendFly( this );
194 if(pOldPage && pOldPage != FindPageFrame())
195 NotifyBackground(pOldPage, aOld, PrepareHint::FlyFrameLeave);
197 //Fix(3495)
198 InvalidatePos_();
199 InvalidatePage();
200 SetNotifyBack();
201 // #i28701# - reset member <maLastCharRect> and
202 // <mnLastTopOfLine> for to-character anchored objects.
203 ClearCharRectAndTopOfLine();
206 //We need some helper classes to monitor the oscillation and a few functions
207 //to not get lost.
209 namespace {
211 // #i3317# - re-factoring of the position stack
212 class SwOszControl
214 static const SwFlyFrame* s_pStack1;
215 static const SwFlyFrame* s_pStack2;
216 static const SwFlyFrame* s_pStack3;
217 static const SwFlyFrame* s_pStack4;
218 static const SwFlyFrame* s_pStack5;
220 const SwFlyFrame* m_pFly;
221 std::vector<Point> maObjPositions;
223 public:
224 explicit SwOszControl( const SwFlyFrame *pFrame );
225 ~SwOszControl();
226 bool ChkOsz();
227 static bool IsInProgress( const SwFlyFrame *pFly );
232 const SwFlyFrame* SwOszControl::s_pStack1 = nullptr;
233 const SwFlyFrame* SwOszControl::s_pStack2 = nullptr;
234 const SwFlyFrame* SwOszControl::s_pStack3 = nullptr;
235 const SwFlyFrame* SwOszControl::s_pStack4 = nullptr;
236 const SwFlyFrame* SwOszControl::s_pStack5 = nullptr;
238 SwOszControl::SwOszControl(const SwFlyFrame* pFrame)
239 : m_pFly(pFrame)
241 if (!SwOszControl::s_pStack1)
242 SwOszControl::s_pStack1 = m_pFly;
243 else if (!SwOszControl::s_pStack2)
244 SwOszControl::s_pStack2 = m_pFly;
245 else if (!SwOszControl::s_pStack3)
246 SwOszControl::s_pStack3 = m_pFly;
247 else if (!SwOszControl::s_pStack4)
248 SwOszControl::s_pStack4 = m_pFly;
249 else if (!SwOszControl::s_pStack5)
250 SwOszControl::s_pStack5 = m_pFly;
253 SwOszControl::~SwOszControl()
255 if (SwOszControl::s_pStack1 == m_pFly)
256 SwOszControl::s_pStack1 = nullptr;
257 else if (SwOszControl::s_pStack2 == m_pFly)
258 SwOszControl::s_pStack2 = nullptr;
259 else if (SwOszControl::s_pStack3 == m_pFly)
260 SwOszControl::s_pStack3 = nullptr;
261 else if (SwOszControl::s_pStack4 == m_pFly)
262 SwOszControl::s_pStack4 = nullptr;
263 else if (SwOszControl::s_pStack5 == m_pFly)
264 SwOszControl::s_pStack5 = nullptr;
265 // #i3317#
266 maObjPositions.clear();
269 bool SwOszControl::IsInProgress( const SwFlyFrame *pFly )
271 if (SwOszControl::s_pStack1 && !pFly->IsLowerOf(SwOszControl::s_pStack1))
272 return true;
273 if (SwOszControl::s_pStack2 && !pFly->IsLowerOf(SwOszControl::s_pStack2))
274 return true;
275 if (SwOszControl::s_pStack3 && !pFly->IsLowerOf(SwOszControl::s_pStack3))
276 return true;
277 if (SwOszControl::s_pStack4 && !pFly->IsLowerOf(SwOszControl::s_pStack4))
278 return true;
279 if (SwOszControl::s_pStack5 && !pFly->IsLowerOf(SwOszControl::s_pStack5))
280 return true;
281 return false;
284 bool SwOszControl::ChkOsz()
286 bool bOscillationDetected = false;
288 if ( maObjPositions.size() == 20 )
290 // #i3317# position stack is full -> oscillation
291 bOscillationDetected = true;
293 else
295 Point aNewObjPos = m_pFly->GetObjRect().Pos();
296 for ( auto const & pt : maObjPositions )
298 if ( aNewObjPos == pt )
300 // position already occurred -> oscillation
301 bOscillationDetected = true;
302 break;
305 if ( !bOscillationDetected )
307 maObjPositions.push_back( aNewObjPos );
311 return bOscillationDetected;
315 |* With a paragraph-anchored fly it's absolutely possible that
316 |* the anchor reacts to changes of the fly. To this reaction the fly must
317 |* certainly react too. Sadly this can lead to oscillations; for example the
318 |* fly wants to go down therefore the content can go up - this leads to a
319 |* smaller TextFrame thus the fly needs to go up again whereby the text will
320 |* get pushed down...
321 |* To avoid such oscillations, a small position stack is built. If the fly
322 |* reaches a position which it already had once, the action is stopped.
323 |* To not run into problems, the stack is designed to hold five positions.
324 |* If the stack flows over, the action is stopped too.
325 |* Cancellation leads to the situation that the fly has a bad position in
326 |* the end. In case of cancellation, the frame is set to automatic top
327 |* alignment to not trigger a 'big oscillation' when calling from outside
328 |* again.
330 void SwFlyAtContentFrame::MakeAll(vcl::RenderContext* pRenderContext)
332 if ( !GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) )
334 return;
337 if ( SwOszControl::IsInProgress( this ) || IsLocked() || IsColLocked() )
338 return;
340 // #i28701# - use new method <GetPageFrame()>
341 if( !GetPageFrame() && GetAnchorFrame() && GetAnchorFrame()->IsInFly() )
343 SwFlyFrame* pFly = AnchorFrame()->FindFlyFrame();
344 SwPageFrame *pTmpPage = pFly ? pFly->FindPageFrame() : nullptr;
345 if( pTmpPage )
346 pTmpPage->AppendFlyToPage( this );
348 // #i28701# - use new method <GetPageFrame()>
349 if( !GetPageFrame() )
350 return;
352 bSetCompletePaintOnInvalidate = true;
354 SwFlyFrameFormat *pFormat = GetFormat();
355 const SwFormatFrameSize &rFrameSz = GetFormat()->GetFrameSize();
356 if( rFrameSz.GetHeightPercent() != SwFormatFrameSize::SYNCED &&
357 rFrameSz.GetHeightPercent() >= 100 )
359 pFormat->LockModify();
360 SwFormatSurround aMain( pFormat->GetSurround() );
361 if ( aMain.GetSurround() == css::text::WrapTextMode_NONE )
363 aMain.SetSurround( css::text::WrapTextMode_THROUGH );
364 pFormat->SetFormatAttr( aMain );
366 pFormat->UnlockModify();
370 SwOszControl aOszCntrl( this );
372 // #i43255#
373 // #i50356# - format the anchor frame, which
374 // contains the anchor position. E.g., for at-character anchored
375 // object this can be the follow frame of the anchor frame.
376 const bool bFormatAnchor =
377 !static_cast<const SwTextFrame*>( GetAnchorFrameContainingAnchPos() )->IsAnyJoinLocked() &&
378 !ConsiderObjWrapInfluenceOnObjPos() &&
379 !ConsiderObjWrapInfluenceOfOtherObjs();
381 const SwFrame* pFooter = GetAnchorFrame()->FindFooterOrHeader();
382 if( pFooter && !pFooter->IsFooterFrame() )
383 pFooter = nullptr;
384 bool bOsz = false;
385 bool bExtra = Lower() && Lower()->IsColumnFrame();
386 // #i3317# - boolean, to apply temporarily the
387 // 'straightforward positioning process' for the frame due to its
388 // overlapping with a previous column.
389 bool bConsiderWrapInfluenceDueToOverlapPrevCol( false );
390 // #i35911# - boolean, to apply temporarily the
391 // 'straightforward positioning process' for the frame due to fact
392 // that it causes the complete content of its layout environment
393 // to move forward.
394 // #i40444# - extend usage of this boolean:
395 // apply temporarily the 'straightforward positioning process' for
396 // the frame due to the fact that the frame clears the area for
397 // the anchor frame, thus it has to move forward.
398 bool bConsiderWrapInfluenceDueToMovedFwdAnchor( false );
399 do {
400 SwRectFnSet aRectFnSet(this);
401 Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
402 SwFlyFreeFrame::MakeAll(pRenderContext);
404 const bool bPosChgDueToOwnFormat =
405 aOldPos != aRectFnSet.GetPos(getFrameArea());
406 // #i3317#
407 if ( !ConsiderObjWrapInfluenceOnObjPos() &&
408 OverlapsPrevColumn() )
410 bConsiderWrapInfluenceDueToOverlapPrevCol = true;
412 // #i28701# - no format of anchor frame, if
413 // wrapping style influence is considered on object positioning
414 if ( bFormatAnchor )
416 SwTextFrame& rAnchPosAnchorFrame =
417 *GetAnchorFrameContainingAnchPos()->DynCastTextFrame();
418 // #i58182# - For the usage of new method
419 // <SwObjectFormatterTextFrame::CheckMovedFwdCondition(..)>
420 // to check move forward of anchor frame due to the object
421 // positioning it's needed to know, if the object is anchored
422 // at the master frame before the anchor frame is formatted.
423 const bool bAnchoredAtMaster(!rAnchPosAnchorFrame.IsFollow());
425 // #i56300#
426 // perform complete format of anchor text frame and its
427 // previous frames, which have become invalid due to the
428 // fly frame format.
429 SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( rAnchPosAnchorFrame );
430 // #i35911#
431 // #i40444#
432 // #i58182# - usage of new method
433 // <SwObjectFormatterTextFrame::CheckMovedFwdCondition(..)>
434 sal_uInt32 nToPageNum( 0 );
435 bool bDummy( false );
436 bool bPageHasFlysAnchoredBelowThis(false);
437 if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition(
438 // TODO: what if this fly moved bc it's in table? does sth prevent that?
439 *this, *GetPageFrame(),
440 bAnchoredAtMaster, nToPageNum, bDummy,
441 bPageHasFlysAnchoredBelowThis) )
443 if (!bPageHasFlysAnchoredBelowThis)
445 bConsiderWrapInfluenceDueToMovedFwdAnchor = true;
447 // mark anchor text frame
448 // directly, that it is moved forward by object positioning.
449 SwTextFrame* pAnchorTextFrame( static_cast<SwTextFrame*>(AnchorFrame()) );
450 bool bInsert( true );
451 sal_uInt32 nAnchorFrameToPageNum( 0 );
452 const SwDoc& rDoc = *(GetFrameFormat().GetDoc());
453 if ( SwLayouter::FrameMovedFwdByObjPos(
454 rDoc, *pAnchorTextFrame, nAnchorFrameToPageNum ) )
456 if ( nAnchorFrameToPageNum < nToPageNum )
458 if (!bPageHasFlysAnchoredBelowThis)
460 SwLayouter::RemoveMovedFwdFrame(rDoc, *pAnchorTextFrame);
463 else
464 bInsert = false;
466 if ( bInsert )
468 if (!bPageHasFlysAnchoredBelowThis)
470 SwLayouter::InsertMovedFwdFrame(rDoc, *pAnchorTextFrame,
471 nToPageNum);
477 if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) ||
478 ( !isFrameAreaPositionValid() &&
479 ( pFooter || bPosChgDueToOwnFormat ) ) )
481 bOsz = aOszCntrl.ChkOsz();
483 // special loop prevention for dedicated document:
484 if ( bOsz &&
485 HasFixSize() && IsClipped() &&
486 GetAnchorFrame()->GetUpper()->IsCellFrame() )
488 SwFrameFormat* pFormat = GetFormat();
489 const SwFormatFrameSize& rFrameSz = pFormat->GetFrameSize();
490 if ( rFrameSz.GetWidthPercent() &&
491 rFrameSz.GetHeightPercent() == SwFormatFrameSize::SYNCED )
493 SwFormatSurround aSurround( pFormat->GetSurround() );
494 if ( aSurround.GetSurround() == css::text::WrapTextMode_NONE )
496 pFormat->LockModify();
497 aSurround.SetSurround( css::text::WrapTextMode_THROUGH );
498 pFormat->SetFormatAttr( aSurround );
499 pFormat->UnlockModify();
500 bOsz = false;
501 OSL_FAIL( "<SwFlyAtContentFrame::MakeAll()> - special loop prevention for dedicated document of b6403541 applied" );
507 if ( bExtra && Lower() && !Lower()->isFrameAreaPositionValid() )
509 // If a multi column frame leaves invalid columns because of
510 // a position change, we loop once more and format
511 // our content using FormatWidthCols again.
512 InvalidateSize_();
513 bExtra = false; // Ensure only one additional loop run
515 } while ( !isFrameAreaDefinitionValid() && !bOsz &&
516 // #i3317#
517 !bConsiderWrapInfluenceDueToOverlapPrevCol &&
518 // #i40444#
519 !bConsiderWrapInfluenceDueToMovedFwdAnchor &&
520 GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) );
522 // #i3317# - instead of attribute change apply
523 // temporarily the 'straightforward positioning process'.
524 // #i80924#
525 // handle special case during splitting of table rows
526 if ( bConsiderWrapInfluenceDueToMovedFwdAnchor &&
527 GetAnchorFrame()->IsInTab() &&
528 GetAnchorFrame()->IsInFollowFlowRow() )
530 const SwFrame* pCellFrame = GetAnchorFrame();
531 while ( pCellFrame && !pCellFrame->IsCellFrame() )
533 pCellFrame = pCellFrame->GetUpper();
535 if ( pCellFrame )
537 SwRectFnSet aRectFnSet(pCellFrame);
538 if ( aRectFnSet.GetTop(pCellFrame->getFrameArea()) == 0 &&
539 aRectFnSet.GetHeight(pCellFrame->getFrameArea()) == 0 )
541 bConsiderWrapInfluenceDueToMovedFwdAnchor = false;
545 // tdf#137803: Fix the position of the shape during autoSize
546 SwFrameFormat* pShapeFormat
547 = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT);
548 // FIXME: According to tdf37153, ignore FollowTextFlow objs, because
549 // wrong position will applied in that case. FollowTextFlow needs fix.
550 if (pShapeFormat && !pShapeFormat->GetFollowTextFlow().GetValue() &&
551 SwTextBoxHelper::getProperty(pShapeFormat,
552 UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).hasValue() &&
553 SwTextBoxHelper::getProperty(pShapeFormat,
554 UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).get<bool>() )
556 // get the text area of the shape
557 const tools::Rectangle aTextRectangle
558 = SwTextBoxHelper::getRelativeTextRectangle(pShapeFormat->FindRealSdrObject());
559 // get the original textframe position
560 SwFormatHoriOrient aHOri = pShapeFormat->GetHoriOrient();
561 SwFormatVertOrient aVOri = pShapeFormat->GetVertOrient();
562 // calc the right position of the shape depending on text area
563 aHOri.SetPos(aHOri.GetPos() + aTextRectangle.Left());
564 aVOri.SetPos(aVOri.GetPos() + aTextRectangle.Top());
565 // save the new position for the shape
566 auto pFormat = GetFormat();
567 const bool bLocked = pFormat->IsModifyLocked();
568 if (!bLocked)
569 pFormat->LockModify();
570 pFormat->SetFormatAttr(aHOri);
571 pFormat->SetFormatAttr(aVOri);
572 if (!bLocked)
573 pFormat->UnlockModify();
575 if ( bOsz || bConsiderWrapInfluenceDueToOverlapPrevCol ||
576 // #i40444#
577 bConsiderWrapInfluenceDueToMovedFwdAnchor )
579 SetTmpConsiderWrapInfluence( true );
580 SetRestartLayoutProcess( true );
581 SetTmpConsiderWrapInfluenceOfOtherObjs();
583 bSetCompletePaintOnInvalidate = false;
586 /** method to determine, if a <MakeAll()> on the Writer fly frame is possible
588 #i28701#
590 bool SwFlyAtContentFrame::IsFormatPossible() const
592 return SwFlyFreeFrame::IsFormatPossible() &&
593 !SwOszControl::IsInProgress( this );
596 namespace {
598 class SwDistance
600 public:
601 SwTwips m_nMain, m_nSub;
602 SwDistance()
603 : m_nMain(0)
604 , m_nSub(0)
607 bool operator<( const SwDistance& rTwo ) const
609 return m_nMain < rTwo.m_nMain
610 || (m_nMain == rTwo.m_nMain && m_nSub && rTwo.m_nSub && m_nSub < rTwo.m_nSub);
612 bool operator<=( const SwDistance& rTwo ) const
614 return m_nMain < rTwo.m_nMain
615 || (m_nMain == rTwo.m_nMain
616 && (!m_nSub || !rTwo.m_nSub || m_nSub <= rTwo.m_nSub));
622 static const SwFrame * lcl_CalcDownDist( SwDistance &rRet,
623 const Point &rPt,
624 const SwContentFrame *pCnt )
626 rRet.m_nSub = 0;
627 //If the point stays inside the Cnt everything is clear already; the Content
628 //automatically has a distance of 0.
629 if ( pCnt->getFrameArea().Contains( rPt ) )
631 rRet.m_nMain = 0;
632 return pCnt;
634 else
636 const SwLayoutFrame *pUp = pCnt->IsInTab() ? pCnt->FindTabFrame()->GetUpper() : pCnt->GetUpper();
637 // single column sections need to interconnect to their upper
638 while( pUp->IsSctFrame() )
639 pUp = pUp->GetUpper();
640 const bool bVert = pUp->IsVertical();
642 const bool bVertL2R = pUp->IsVertLR();
644 //Follow the text flow.
645 // #i70582#
646 // --> OD 2009-03-05 - adopted for Support for Classical Mongolian Script
647 const SwTwips nTopForObjPos = lcl_GetTopForObjPos(pCnt, bVert, bVertL2R);
648 if ( pUp->getFrameArea().Contains( rPt ) )
650 // <rPt> point is inside environment of given content frame
651 // #i70582#
652 if( bVert )
654 if ( bVertL2R )
655 rRet.m_nMain = rPt.X() - nTopForObjPos;
656 else
657 rRet.m_nMain = nTopForObjPos - rPt.X();
659 else
660 rRet.m_nMain = rPt.Y() - nTopForObjPos;
661 return pCnt;
663 else if ( rPt.Y() <= pUp->getFrameArea().Top() )
665 // <rPt> point is above environment of given content frame
666 // correct for vertical layout?
667 rRet.m_nMain = LONG_MAX;
669 else if( rPt.X() < pUp->getFrameArea().Left() &&
670 rPt.Y() <= ( bVert ? pUp->getFrameArea().Top() : pUp->getFrameArea().Bottom() ) )
672 // <rPt> point is left of environment of given content frame
673 // seems not to be correct for vertical layout!?
674 const SwFrame *pLay = pUp->GetLeaf( MAKEPAGE_NONE, false, pCnt );
675 if( !pLay ||
676 (bVert && (pLay->getFrameArea().Top() + pLay->getFramePrintArea().Bottom()) <rPt.Y())||
677 (!bVert && (pLay->getFrameArea().Left() + pLay->getFramePrintArea().Right())<rPt.X()) )
679 // <rPt> point is in left border of environment
680 // #i70582#
681 if( bVert )
683 if ( bVertL2R )
684 rRet.m_nMain = rPt.X() - nTopForObjPos;
685 else
686 rRet.m_nMain = nTopForObjPos - rPt.X();
688 else
689 rRet.m_nMain = rPt.Y() - nTopForObjPos;
690 return pCnt;
692 else
693 rRet.m_nMain = LONG_MAX;
695 else
697 rRet.m_nMain
698 = bVert ? (bVertL2R
699 ? ((pUp->getFrameArea().Left() + pUp->getFramePrintArea().Right())
700 - nTopForObjPos)
701 : (nTopForObjPos
702 - (pUp->getFrameArea().Left() + pUp->getFramePrintArea().Left())))
703 : ((pUp->getFrameArea().Top() + pUp->getFramePrintArea().Bottom())
704 - nTopForObjPos);
706 const SwFrame *pPre = pCnt;
707 const SwFrame *pLay = pUp->GetLeaf( MAKEPAGE_NONE, true, pCnt );
708 SwTwips nFrameTop = 0;
709 SwTwips nPrtHeight = 0;
710 bool bSct = false;
711 const SwSectionFrame *pSect = pUp->FindSctFrame();
712 if( pSect )
714 rRet.m_nSub = rRet.m_nMain;
715 rRet.m_nMain = 0;
717 if( pSect && !pSect->IsAnLower( pLay ) )
719 bSct = false;
720 const SwSectionFrame* pNxtSect = pLay ? pLay->FindSctFrame() : nullptr;
721 if (pSect->IsAnFollow(pNxtSect) && pLay)
723 if( pLay->IsVertical() )
725 if ( pLay->IsVertLR() )
726 nFrameTop = pLay->getFrameArea().Left();
727 else
728 nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
729 nPrtHeight = pLay->getFramePrintArea().Width();
731 else
733 nFrameTop = pLay->getFrameArea().Top();
734 nPrtHeight = pLay->getFramePrintArea().Height();
736 pSect = pNxtSect;
738 else
740 pLay = pSect->GetUpper();
741 if( pLay->IsVertical() )
743 if ( pLay->IsVertLR() )
745 nFrameTop = pSect->getFrameArea().Right();
746 nPrtHeight = pLay->getFrameArea().Left() + pLay->getFramePrintArea().Left()
747 + pLay->getFramePrintArea().Width() - pSect->getFrameArea().Left()
748 - pSect->getFrameArea().Width();
750 else
752 nFrameTop = pSect->getFrameArea().Left();
753 nPrtHeight = pSect->getFrameArea().Left() - pLay->getFrameArea().Left()
754 - pLay->getFramePrintArea().Left();
757 else
759 nFrameTop = pSect->getFrameArea().Bottom();
760 nPrtHeight = pLay->getFrameArea().Top() + pLay->getFramePrintArea().Top()
761 + pLay->getFramePrintArea().Height() - pSect->getFrameArea().Top()
762 - pSect->getFrameArea().Height();
764 pSect = nullptr;
767 else if( pLay )
769 if( pLay->IsVertical() )
771 if ( pLay->IsVertLR() )
773 nFrameTop = pLay->getFrameArea().Left();
774 nPrtHeight = pLay->getFramePrintArea().Width();
776 else
778 nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
779 nPrtHeight = pLay->getFramePrintArea().Width();
782 else
784 nFrameTop = pLay->getFrameArea().Top();
785 nPrtHeight = pLay->getFramePrintArea().Height();
787 bSct = nullptr != pSect;
789 while ( pLay && !pLay->getFrameArea().Contains( rPt ) &&
790 ( pLay->getFrameArea().Top() <= rPt.Y() || pLay->IsInFly() ||
791 ( pLay->IsInSct() &&
792 pLay->FindSctFrame()->GetUpper()->getFrameArea().Top() <= rPt.Y())) )
794 if ( pLay->IsFootnoteContFrame() )
796 if ( !static_cast<const SwLayoutFrame*>(pLay)->Lower() )
798 SwFrame *pDel = const_cast<SwFrame*>(pLay);
799 pDel->Cut();
800 SwFrame::DestroyFrame(pDel);
801 return pPre;
803 return nullptr;
805 else
807 if( bSct || pSect )
808 rRet.m_nSub += nPrtHeight;
809 else
810 rRet.m_nMain += nPrtHeight;
811 pPre = pLay;
812 pLay = pLay->GetLeaf( MAKEPAGE_NONE, true, pCnt );
813 if( pSect && !pSect->IsAnLower( pLay ) )
814 { // If we're leaving a SwSectionFrame, the next Leaf-Frame
815 // is the part of the upper below the SectionFrame.
816 const SwSectionFrame* pNxtSect = pLay ?
817 pLay->FindSctFrame() : nullptr;
818 bSct = false;
819 if (pLay && pSect->IsAnFollow(pNxtSect))
821 pSect = pNxtSect;
822 if( pLay->IsVertical() )
824 if ( pLay->IsVertLR() )
826 nFrameTop = pLay->getFrameArea().Left();
827 nPrtHeight = pLay->getFramePrintArea().Width();
829 else
831 nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
832 nPrtHeight = pLay->getFramePrintArea().Width();
835 else
837 nFrameTop = pLay->getFrameArea().Top();
838 nPrtHeight = pLay->getFramePrintArea().Height();
841 else
843 pLay = pSect->GetUpper();
844 if( pLay->IsVertical() )
846 if ( pLay->IsVertLR() )
848 nFrameTop = pSect->getFrameArea().Right();
849 nPrtHeight = pLay->getFrameArea().Left()+pLay->getFramePrintArea().Left()
850 + pLay->getFramePrintArea().Width() - pSect->getFrameArea().Left()
851 - pSect->getFrameArea().Width();
853 else
855 nFrameTop = pSect->getFrameArea().Left();
856 nPrtHeight = pSect->getFrameArea().Left() -
857 pLay->getFrameArea().Left() - pLay->getFramePrintArea().Left();
860 else
862 nFrameTop = pSect->getFrameArea().Bottom();
863 nPrtHeight = pLay->getFrameArea().Top()+pLay->getFramePrintArea().Top()
864 + pLay->getFramePrintArea().Height() - pSect->getFrameArea().Top()
865 - pSect->getFrameArea().Height();
867 pSect = nullptr;
870 else if( pLay )
872 if( pLay->IsVertical() )
874 if ( pLay->IsVertLR() )
876 nFrameTop = pLay->getFrameArea().Left();
877 nPrtHeight = pLay->getFramePrintArea().Width();
879 else
881 nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
882 nPrtHeight = pLay->getFramePrintArea().Width();
885 else
887 nFrameTop = pLay->getFrameArea().Top();
888 nPrtHeight = pLay->getFramePrintArea().Height();
890 bSct = nullptr != pSect;
894 if ( pLay )
896 if ( pLay->getFrameArea().Contains( rPt ) )
898 SwTwips nDiff = pLay->IsVertical() ? ( pLay->IsVertLR() ? ( rPt.X() - nFrameTop ) : ( nFrameTop - rPt.X() ) )
899 : ( rPt.Y() - nFrameTop );
900 if( bSct || pSect )
901 rRet.m_nSub += nDiff;
902 else
903 rRet.m_nMain += nDiff;
905 if ( pLay->IsFootnoteContFrame() && !static_cast<const SwLayoutFrame*>(pLay)->Lower() )
907 SwFrame *pDel = const_cast<SwFrame*>(pLay);
908 pDel->Cut();
909 SwFrame::DestroyFrame(pDel);
910 return nullptr;
912 return pLay;
914 else
915 rRet.m_nMain = LONG_MAX;
918 return nullptr;
921 static sal_uInt64 lcl_FindCntDiff( const Point &rPt, const SwLayoutFrame *pLay,
922 const SwContentFrame *& rpCnt,
923 const bool bBody, const bool bFootnote )
925 // Searches below pLay the nearest Cnt to the point. The reference point of
926 //the Contents is always the left upper corner.
927 //The Cnt should preferably be above the point.
929 rpCnt = nullptr;
930 sal_uInt64 nDistance = SAL_MAX_UINT64;
931 sal_uInt64 nNearest = SAL_MAX_UINT64;
932 const SwContentFrame *pCnt = pLay ? pLay->ContainsContent() : nullptr;
934 while ( pCnt && (bBody != pCnt->IsInDocBody() || bFootnote != pCnt->IsInFootnote()))
936 pCnt = pCnt->GetNextContentFrame();
937 if ( !pLay->IsAnLower( pCnt ) )
938 pCnt = nullptr;
940 const SwContentFrame *pNearest = pCnt;
941 if ( pCnt )
945 //Calculate the distance between those two points.
946 //'delta' X^2 + 'delta' Y^2 = 'distance'^2
947 sal_uInt64 dX = std::max( pCnt->getFrameArea().Left(), rPt.X() ) -
948 std::min( pCnt->getFrameArea().Left(), rPt.X() ),
949 dY = std::max( pCnt->getFrameArea().Top(), rPt.Y() ) -
950 std::min( pCnt->getFrameArea().Top(), rPt.Y() );
951 // square of the difference will do fine here
952 const sal_uInt64 nDiff = (dX * dX) + (dY * dY);
953 if ( pCnt->getFrameArea().Top() <= rPt.Y() )
955 if ( nDiff < nDistance )
957 //This one is the nearer one
958 nDistance = nNearest = nDiff;
959 rpCnt = pNearest = pCnt;
962 else if ( nDiff < nNearest )
964 nNearest = nDiff;
965 pNearest = pCnt;
967 pCnt = pCnt->GetNextContentFrame();
968 while ( pCnt &&
969 (bBody != pCnt->IsInDocBody() || bFootnote != pCnt->IsInFootnote()))
970 pCnt = pCnt->GetNextContentFrame();
972 } while ( pCnt && pLay->IsAnLower( pCnt ) );
974 if (nDistance == SAL_MAX_UINT64)
975 { rpCnt = pNearest;
976 return nNearest;
978 return nDistance;
981 static const SwContentFrame * lcl_FindCnt( const Point &rPt, const SwContentFrame *pCnt,
982 const bool bBody, const bool bFootnote )
984 //Starting from pCnt searches the ContentFrame whose left upper corner is the
985 //nearest to the point.
986 //Always returns a ContentFrame.
988 //First the nearest Content inside the page which contains the Content is
989 //searched. Starting from this page the pages in both directions need to
990 //be considered. If possible a Content is returned whose Y-position is
991 //above the point.
992 const SwContentFrame *pRet, *pNew;
993 const SwLayoutFrame *pLay = pCnt->FindPageFrame();
994 sal_uInt64 nDist; // not sure if a sal_Int32 would be enough?
996 nDist = ::lcl_FindCntDiff( rPt, pLay, pNew, bBody, bFootnote );
997 if ( pNew )
998 pRet = pNew;
999 else
1000 { pRet = pCnt;
1001 nDist = SAL_MAX_UINT64;
1003 const SwContentFrame *pNearest = pRet;
1004 sal_uInt64 nNearest = nDist;
1006 if ( pLay )
1008 const SwLayoutFrame *pPge = pLay;
1009 sal_uInt64 nOldNew = SAL_MAX_UINT64;
1010 for ( int i = 0; pPge->GetPrev() && (i < 3); ++i )
1012 pPge = static_cast<const SwLayoutFrame*>(pPge->GetPrev());
1013 const sal_uInt64 nNew = ::lcl_FindCntDiff( rPt, pPge, pNew, bBody, bFootnote );
1014 if ( nNew < nDist )
1016 if ( pNew->getFrameArea().Top() <= rPt.Y() )
1018 pRet = pNearest = pNew;
1019 nDist = nNearest = nNew;
1021 else if ( nNew < nNearest )
1023 pNearest = pNew;
1024 nNearest = nNew;
1027 else if (nOldNew != SAL_MAX_UINT64 && nNew > nOldNew)
1028 break;
1029 else
1030 nOldNew = nNew;
1033 pPge = pLay;
1034 nOldNew = SAL_MAX_UINT64;
1035 for ( int j = 0; pPge->GetNext() && (j < 3); ++j )
1037 pPge = static_cast<const SwLayoutFrame*>(pPge->GetNext());
1038 const sal_uInt64 nNew = ::lcl_FindCntDiff( rPt, pPge, pNew, bBody, bFootnote );
1039 if ( nNew < nDist )
1041 if ( pNew->getFrameArea().Top() <= rPt.Y() )
1043 pRet = pNearest = pNew;
1044 nDist = nNearest = nNew;
1046 else if ( nNew < nNearest )
1048 pNearest = pNew;
1049 nNearest = nNew;
1052 else if (nOldNew != SAL_MAX_UINT64 && nNew > nOldNew)
1053 break;
1054 else
1055 nOldNew = nNew;
1058 if ( pRet->getFrameArea().Top() > rPt.Y() )
1059 return pNearest;
1060 else
1061 return pRet;
1064 static void lcl_PointToPrt( Point &rPoint, const SwFrame *pFrame )
1066 SwRect aTmp( pFrame->getFramePrintArea() );
1067 aTmp += pFrame->getFrameArea().Pos();
1068 if ( rPoint.getX() < aTmp.Left() )
1069 rPoint.setX(aTmp.Left());
1070 else if ( rPoint.getX() > aTmp.Right() )
1071 rPoint.setX(aTmp.Right());
1072 if ( rPoint.getY() < aTmp.Top() )
1073 rPoint.setY(aTmp.Top());
1074 else if ( rPoint.getY() > aTmp.Bottom() )
1075 rPoint.setY(aTmp.Bottom());
1079 /** Searches an anchor for paragraph bound objects starting from pOldAnch.
1081 * This is used to show anchors as well as changing anchors
1082 * when dragging paragraph bound objects.
1084 const SwContentFrame *FindAnchor( const SwFrame *pOldAnch, const Point &rNew,
1085 const bool bBodyOnly )
1087 //Search the nearest Cnt around the given document position in the text
1088 //flow. The given anchor is the starting Frame.
1089 const SwContentFrame* pCnt;
1090 if ( pOldAnch->IsContentFrame() )
1092 pCnt = static_cast<const SwContentFrame*>(pOldAnch);
1094 else
1096 Point aTmp( rNew );
1097 const SwLayoutFrame *pTmpLay = static_cast<const SwLayoutFrame*>(pOldAnch);
1098 if( pTmpLay->IsRootFrame() )
1100 SwRect aTmpRect( aTmp, Size(0,0) );
1101 pTmpLay = static_cast<const SwLayoutFrame*>(::FindPage( aTmpRect, pTmpLay->Lower() ));
1103 pCnt = pTmpLay->GetContentPos( aTmp, false, bBodyOnly );
1106 //Take care to use meaningful ranges during search. This means to not enter
1107 //or leave header/footer in this case.
1108 const bool bBody = pCnt->IsInDocBody() || bBodyOnly;
1109 const bool bFootnote = !bBodyOnly && pCnt->IsInFootnote();
1111 Point aNew( rNew );
1112 if ( bBody )
1114 //#38848 drag from page margin into the body.
1115 const SwFrame *pPage = pCnt->FindPageFrame();
1116 ::lcl_PointToPrt( aNew, pPage->GetUpper() );
1117 SwRect aTmp( aNew, Size( 0, 0 ) );
1118 pPage = ::FindPage( aTmp, pPage );
1119 ::lcl_PointToPrt( aNew, pPage );
1122 if ( pCnt->IsInDocBody() == bBody && pCnt->getFrameArea().Contains( aNew ) )
1123 return pCnt;
1124 else if ( pOldAnch->IsInDocBody() || pOldAnch->IsPageFrame() )
1126 // Maybe the selected anchor is on the same page as the current anchor.
1127 // With this we won't run into problems with the columns.
1128 Point aTmp( aNew );
1129 const SwContentFrame *pTmp = pCnt->FindPageFrame()->
1130 GetContentPos( aTmp, false, true );
1131 if ( pTmp && pTmp->getFrameArea().Contains( aNew ) )
1132 return pTmp;
1135 //Starting from the anchor we now search in both directions until we found
1136 //the nearest one respectively.
1137 //Not the direct distance is relevant but the distance which needs to be
1138 //traveled through the text flow.
1139 const SwContentFrame *pUpLst;
1140 const SwContentFrame *pUpFrame = pCnt;
1141 SwDistance nUp, nUpLst;
1142 ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
1143 SwDistance nDown = nUp;
1144 bool bNegAllowed = true;// Make it possible to leave the negative section once.
1147 pUpLst = pUpFrame; nUpLst = nUp;
1148 pUpFrame = pUpLst->GetPrevContentFrame();
1149 while ( pUpFrame &&
1150 (bBody != pUpFrame->IsInDocBody() || bFootnote != pUpFrame->IsInFootnote()))
1151 pUpFrame = pUpFrame->GetPrevContentFrame();
1152 if ( pUpFrame )
1154 ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
1155 //It makes sense to search further, if the distance grows inside
1156 //a table.
1157 if ( pUpLst->IsInTab() && pUpFrame->IsInTab() )
1159 while ( pUpFrame && ((nUpLst < nUp && pUpFrame->IsInTab()) ||
1160 bBody != pUpFrame->IsInDocBody()) )
1162 pUpFrame = pUpFrame->GetPrevContentFrame();
1163 if ( pUpFrame )
1164 ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
1168 if ( !pUpFrame )
1169 nUp.m_nMain = LONG_MAX;
1170 if (nUp.m_nMain >= 0 && LONG_MAX != nUp.m_nMain)
1172 bNegAllowed = false;
1173 if (nUpLst.m_nMain < 0) //don't take the wrong one, if the value
1174 //just changed from negative to positive.
1175 { pUpLst = pUpFrame;
1176 nUpLst = nUp;
1179 } while (pUpFrame && ((bNegAllowed && nUp.m_nMain < 0) || (nUp <= nUpLst)));
1181 const SwContentFrame *pDownLst;
1182 const SwContentFrame *pDownFrame = pCnt;
1183 SwDistance nDownLst;
1184 if (nDown.m_nMain < 0)
1185 nDown.m_nMain = LONG_MAX;
1188 pDownLst = pDownFrame; nDownLst = nDown;
1189 pDownFrame = pDownLst->GetNextContentFrame();
1190 while ( pDownFrame &&
1191 (bBody != pDownFrame->IsInDocBody() || bFootnote != pDownFrame->IsInFootnote()))
1192 pDownFrame = pDownFrame->GetNextContentFrame();
1193 if ( pDownFrame )
1195 ::lcl_CalcDownDist( nDown, aNew, pDownFrame );
1196 if (nDown.m_nMain < 0)
1197 nDown.m_nMain = LONG_MAX;
1198 //It makes sense to search further, if the distance grows inside
1199 //a table.
1200 if ( pDownLst->IsInTab() && pDownFrame->IsInTab() )
1202 while (pDownFrame
1203 && ((nDown.m_nMain != LONG_MAX && pDownFrame->IsInTab())
1204 || bBody != pDownFrame->IsInDocBody()))
1206 pDownFrame = pDownFrame->GetNextContentFrame();
1207 if ( pDownFrame )
1208 ::lcl_CalcDownDist( nDown, aNew, pDownFrame );
1209 if (nDown.m_nMain < 0)
1210 nDown.m_nMain = LONG_MAX;
1214 if ( !pDownFrame )
1215 nDown.m_nMain = LONG_MAX;
1217 } while (pDownFrame && nDown <= nDownLst && nDown.m_nMain != LONG_MAX
1218 && nDownLst.m_nMain != LONG_MAX);
1220 //If we couldn't find one in both directions, we'll search the Content whose
1221 //left upper corner is the nearest to the point. Such a situation may
1222 //happen, if the point doesn't lay in the text flow but in any margin.
1223 if (nDownLst.m_nMain == LONG_MAX && nUpLst.m_nMain == LONG_MAX)
1225 // If an OLE objects, which is contained in a fly frame
1226 // is resized in inplace mode and the new Position is outside the
1227 // fly frame, we do not want to leave our fly frame.
1228 if ( pCnt->IsInFly() )
1229 return pCnt;
1231 return ::lcl_FindCnt( aNew, pCnt, bBody, bFootnote );
1233 else
1234 return nDownLst < nUpLst ? pDownLst : pUpLst;
1237 void SwFlyAtContentFrame::SetAbsPos( const Point &rNew )
1239 SwPageFrame *pOldPage = FindPageFrame();
1240 const SwRect aOld( GetObjRectWithSpaces() );
1241 Point aNew( rNew );
1243 if( ( GetAnchorFrame()->IsVertical() && !GetAnchorFrame()->IsVertLR() ) || GetAnchorFrame()->IsRightToLeft() )
1244 aNew.setX(aNew.getX() + getFrameArea().Width());
1245 SwContentFrame *pCnt = const_cast<SwContentFrame*>(::FindAnchor( GetAnchorFrame(), aNew ));
1246 if( pCnt->IsProtected() )
1247 pCnt = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetAnchorFrame()));
1249 SwPageFrame *pTmpPage = nullptr;
1250 const bool bVert = pCnt->IsVertical();
1252 const bool bVertL2R = pCnt->IsVertLR();
1253 const bool bRTL = pCnt->IsRightToLeft();
1255 if( ( !bVert != !GetAnchorFrame()->IsVertical() ) ||
1256 ( !bRTL != !GetAnchorFrame()->IsRightToLeft() ) )
1258 if( bVert || bRTL )
1259 aNew.setX(aNew.getX() + getFrameArea().Width());
1260 else
1261 aNew.setX(aNew.getX() - getFrameArea().Width());
1264 if ( pCnt->IsInDocBody() )
1266 //#38848 drag from page margin into the body.
1267 pTmpPage = pCnt->FindPageFrame();
1268 ::lcl_PointToPrt( aNew, pTmpPage->GetUpper() );
1269 SwRect aTmp( aNew, Size( 0, 0 ) );
1270 pTmpPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aTmp, pTmpPage )));
1271 ::lcl_PointToPrt( aNew, pTmpPage );
1274 //Setup RelPos, only invalidate if requested.
1275 //rNew is an absolute position. We need to calculate the distance from rNew
1276 //to the anchor inside the text flow to correctly set RelPos.
1277 //!!!!!We can optimize here: FindAnchor could also return RelPos!
1278 const SwFrame *pFrame = nullptr;
1279 SwTwips nY;
1280 if ( pCnt->getFrameArea().Contains( aNew ) )
1282 // #i70582#
1283 if ( bVert )
1285 nY = pCnt->getFrameArea().Left() - rNew.X();
1286 if ( bVertL2R )
1287 nY = -nY;
1288 else
1289 nY += pCnt->getFrameArea().Width() - getFrameArea().Width();
1290 nY -= pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
1292 else
1293 nY = rNew.Y() - pCnt->getFrameArea().Top() - pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
1295 else
1297 SwDistance aDist;
1298 pFrame = ::lcl_CalcDownDist( aDist, aNew, pCnt );
1299 nY = aDist.m_nMain + aDist.m_nSub;
1302 SwTwips nX = 0;
1304 if ( pCnt->IsFollow() )
1306 // Flys are never attached to the follow but always to the master,
1307 // which we're going to search now.
1308 const SwContentFrame *pOriginal = pCnt;
1309 const SwContentFrame *pFollow = pCnt;
1310 while ( pCnt->IsFollow() )
1314 SwContentFrame* pPrev = pCnt->GetPrevContentFrame();
1315 if (!pPrev)
1317 SAL_WARN("sw.core", "very unexpected missing PrevContentFrame");
1318 break;
1320 pCnt = pPrev;
1322 while ( pCnt->GetFollow() != pFollow );
1323 pFollow = pCnt;
1325 SwTwips nDiff = 0;
1327 { const SwFrame *pUp = pFollow->GetUpper();
1328 if( pUp->IsVertical() )
1330 if ( pUp->IsVertLR() )
1331 nDiff += pUp->getFramePrintArea().Width() - pFollow->GetRelPos().getX();
1332 else
1333 nDiff += pFollow->getFrameArea().Left() + pFollow->getFrameArea().Width()
1334 - pUp->getFrameArea().Left() - pUp->getFramePrintArea().Left();
1336 else
1337 nDiff += pUp->getFramePrintArea().Height() - pFollow->GetRelPos().Y();
1338 pFollow = pFollow->GetFollow();
1339 } while ( pFollow != pOriginal );
1340 nY += nDiff;
1341 if( bVert )
1342 nX = pCnt->getFrameArea().Top() - pOriginal->getFrameArea().Top();
1343 else
1344 nX = pCnt->getFrameArea().Left() - pOriginal->getFrameArea().Left();
1347 if ( nY == LONG_MAX )
1349 // #i70582#
1350 const SwTwips nTopForObjPos = lcl_GetTopForObjPos(pCnt, bVert, bVertL2R);
1351 if( bVert )
1353 if ( bVertL2R )
1354 nY = rNew.X() - nTopForObjPos;
1355 else
1356 nY = nTopForObjPos - rNew.X();
1358 else
1360 nY = rNew.Y() - nTopForObjPos;
1364 SwFlyFrameFormat *pFormat = GetFormat();
1366 if( bVert )
1368 if( !pFrame )
1369 nX += rNew.Y() - pCnt->getFrameArea().Top();
1370 else
1371 nX = rNew.Y() - pFrame->getFrameArea().Top();
1373 else
1375 if( !pFrame )
1377 if ( pCnt->IsRightToLeft() )
1378 nX += pCnt->getFrameArea().Right() - rNew.X() - getFrameArea().Width();
1379 else
1380 nX += rNew.X() - pCnt->getFrameArea().Left();
1382 else
1384 if ( pFrame->IsRightToLeft() )
1385 nX += pFrame->getFrameArea().Right() - rNew.X() - getFrameArea().Width();
1386 else
1387 nX = rNew.X() - pFrame->getFrameArea().Left();
1390 GetFormat()->GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
1392 if( pCnt != GetAnchorFrame() || ( IsAutoPos() && pCnt->IsTextFrame() &&
1393 GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)) )
1395 //Set the anchor attribute according to the new Cnt.
1396 SwFormatAnchor aAnch( pFormat->GetAnchor() );
1397 SwPosition pos = *aAnch.GetContentAnchor();
1398 if( IsAutoPos() && pCnt->IsTextFrame() )
1400 SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
1401 SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
1402 Point aPt( rNew );
1403 if( pCnt->GetModelPositionForViewPoint( &pos, aPt, &eTmpState )
1404 && FrameContainsNode(*pTextFrame, pos.GetNodeIndex()))
1406 const SwTextAttr *const pTextInputField =
1407 pos.GetNode().GetTextNode()->GetTextAttrAt(
1408 pos.GetContentIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent);
1409 if (pTextInputField != nullptr)
1411 pos.SetContent( pTextInputField->GetStart() );
1413 ResetLastCharRectHeight();
1414 if( text::RelOrientation::CHAR == pFormat->GetVertOrient().GetRelationOrient() )
1415 nY = LONG_MAX;
1416 if( text::RelOrientation::CHAR == pFormat->GetHoriOrient().GetRelationOrient() )
1417 nX = LONG_MAX;
1419 else
1421 pos = pTextFrame->MapViewToModelPos(TextFrameIndex(0));
1424 else if (pCnt->IsTextFrame())
1426 pos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
1428 else // is that even possible? maybe if there was a change of anchor type from AT_FLY or something?
1430 assert(pCnt->IsNoTextFrame());
1431 pos.Assign(*static_cast<SwNoTextFrame*>(pCnt)->GetNode());
1433 aAnch.SetAnchor( &pos );
1435 // handle change of anchor node:
1436 // if count of the anchor frame also change, the fly frames have to be
1437 // re-created. Thus, delete all fly frames except the <this> before the
1438 // anchor attribute is change and re-create them afterwards.
1440 SwHandleAnchorNodeChg aHandleAnchorNodeChg( *pFormat, aAnch, this );
1441 pFormat->GetDoc()->SetAttr( aAnch, *pFormat );
1444 else if ( pTmpPage && pTmpPage != GetPageFrame() )
1445 GetPageFrame()->MoveFly( this, pTmpPage );
1447 const Point aRelPos = bVert ? Point( -nY, nX ) : Point( nX, nY );
1448 ChgRelPos( aRelPos );
1449 GetFormat()->GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
1451 if ( pOldPage != FindPageFrame() )
1452 ::Notify_Background( GetVirtDrawObj(), pOldPage, aOld, PrepareHint::FlyFrameLeave, false );
1455 /** method to assure that anchored object is registered at the correct
1456 page frame
1458 #i28701#
1459 takes over functionality of deleted method <SwFlyAtContentFrame::AssertPage()>
1461 void SwFlyAtContentFrame::RegisterAtCorrectPage()
1463 SwPageFrame* pPageFrame( nullptr );
1464 if ( GetVertPosOrientFrame() )
1466 pPageFrame = const_cast<SwPageFrame*>(GetVertPosOrientFrame()->FindPageFrame());
1468 if ( pPageFrame && GetPageFrame() != pPageFrame )
1470 RegisterAtPage(*pPageFrame);
1474 void SwFlyAtContentFrame::RegisterAtPage(SwPageFrame & rPageFrame)
1476 assert(GetPageFrame() != &rPageFrame);
1477 if (GetPageFrame())
1479 GetPageFrame()->MoveFly( this, &rPageFrame );
1481 else
1483 rPageFrame.AppendFlyToPage( this );
1487 // #i26791#
1488 void SwFlyAtContentFrame::MakeObjPos()
1490 // if fly frame position is valid, nothing is to do. Thus, return
1491 if ( isFrameAreaPositionValid() )
1493 return;
1496 // #i26791# - validate position flag here.
1497 setFrameAreaPositionValid(true);
1499 // #i35911# - no calculation of new position, if
1500 // anchored object is marked that it clears its environment and its
1501 // environment is already cleared.
1502 // before checking for cleared environment
1503 // check, if member <mpVertPosOrientFrame> is set.
1504 if ( GetVertPosOrientFrame() &&
1505 ClearedEnvironment() && HasClearedEnvironment() )
1507 return;
1510 // use new class to position object
1511 objectpositioning::SwToContentAnchoredObjectPosition
1512 aObjPositioning( *GetVirtDrawObj() );
1513 aObjPositioning.CalcPosition();
1515 SetVertPosOrientFrame ( aObjPositioning.GetVertPosOrientFrame() );
1518 // #i28701#
1519 bool SwFlyAtContentFrame::InvalidationAllowed( const InvalidationType _nInvalid ) const
1521 bool bAllowed( SwFlyFreeFrame::InvalidationAllowed( _nInvalid ) );
1523 // forbiddance of base instance can't be over ruled.
1524 if ( bAllowed )
1526 if ( _nInvalid == INVALID_POS ||
1527 _nInvalid == INVALID_ALL )
1529 bAllowed = InvalidationOfPosAllowed();
1533 return bAllowed;
1536 bool SwFlyAtContentFrame::ShouldBwdMoved(SwLayoutFrame* /*pNewUpper*/, bool& /*rReformat*/)
1538 return false;
1541 const SwFlyAtContentFrame* SwFlyAtContentFrame::GetFollow() const
1543 return static_cast<const SwFlyAtContentFrame*>(SwFlowFrame::GetFollow());
1546 SwFlyAtContentFrame* SwFlyAtContentFrame::GetFollow()
1548 return static_cast<SwFlyAtContentFrame*>(SwFlowFrame::GetFollow());
1551 SwLayoutFrame *SwFrame::GetNextFlyLeaf( MakePageType eMakePage )
1553 auto pFly = dynamic_cast<SwFlyAtContentFrame*>(FindFlyFrame());
1554 assert(pFly && "GetNextFlyLeaf: missing fly frame");
1555 assert(pFly->IsFlySplitAllowed() && "GetNextFlyLeaf: fly split not allowed");
1557 if (pFly->HasFollow())
1559 // If we already have a follow, then no need to create a new one, just use it.
1560 return pFly->GetFollow();
1563 SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
1565 if (!pFlyAnchor)
1567 // In case our fly frame is split already, but not yet moved, then FindAnchorCharFrame()
1568 // won't find the anchor, since it wants a follow anchor, but there is no follow anchor yet.
1569 // In this case work with a plain anchor, so FindSctFrame() works to find out we're in a
1570 // section.
1571 auto pAnchorFrame = const_cast<SwFrame*>(pFly->GetAnchorFrame());
1572 if (pAnchorFrame && pAnchorFrame->IsTextFrame())
1574 pFlyAnchor = static_cast<SwTextFrame*>(pAnchorFrame);
1578 bool bBody = pFlyAnchor && pFlyAnchor->IsInDocBody();
1579 SwLayoutFrame *pLayLeaf = nullptr;
1580 // Look up the first candidate.
1581 SwSectionFrame* pFlyAnchorSection = pFlyAnchor ? pFlyAnchor->FindSctFrame() : nullptr;
1582 bool bNesting = false;
1583 if (pFlyAnchorSection)
1585 // The anchor is in a section.
1586 if (pFlyAnchor)
1588 SwTabFrame* pFlyAnchorTab = pFlyAnchor->FindTabFrame();
1589 if (pFlyAnchorTab)
1591 // The anchor is in table as well.
1592 if (pFlyAnchorTab->FindSctFrame() == pFlyAnchorSection)
1594 // We're in a table-in-section, no anchor move in this case, because that would
1595 // mean we're not in a table anymore.
1596 bNesting = true;
1600 if (!bNesting)
1602 // We can't just move the split anchor to the next page, that would be outside the section.
1603 // Rather split that section as well.
1604 pLayLeaf = pFlyAnchorSection->GetNextSctLeaf(eMakePage);
1607 else if (IsTabFrame())
1609 // If we're in a table, try to find the next frame of the table's last content.
1610 SwFrame* pContent = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
1611 pLayLeaf = pContent ? pContent->GetUpper() : nullptr;
1613 else
1615 pLayLeaf = GetNextLayoutLeaf();
1618 SwLayoutFrame* pOldLayLeaf = nullptr;
1619 while (true)
1621 if (pLayLeaf)
1623 // If we're anchored in a body frame, the candidate has to be in a body frame as well.
1624 bool bLeftBody = bBody && !pLayLeaf->IsInDocBody();
1625 // If the candidate is in a fly, make sure that the candidate is a child of our follow.
1626 bool bLeftFly = pLayLeaf->IsInFly() && pLayLeaf->FindFlyFrame() != pFly->GetFollow();
1627 bool bSameBody = false;
1628 if (bBody && pLayLeaf->IsInDocBody())
1630 // Make sure the candidate is not inside the same body frame, that would prevent
1631 // inserting a new page.
1632 if (pFlyAnchor->FindBodyFrame() == pLayLeaf->FindBodyFrame())
1634 bSameBody = true;
1638 if (bLeftFly && pFlyAnchor && pFlyAnchor->IsInTab()
1639 && FindTabFrame() == pLayLeaf->FindTabFrame())
1641 // This is an inner fly (parent is an inline or a floating table), then the follow
1642 // anchor will be just next to us.
1643 SwLayoutFrame* pFlyAnchorUpper = pFlyAnchor->GetUpper();
1644 pOldLayLeaf = pLayLeaf;
1645 pLayLeaf = pFlyAnchorUpper;
1646 bLeftFly = false;
1647 bNesting = true;
1650 if (bLeftBody || bLeftFly || bSameBody)
1652 // The above conditions are not held, reject.
1653 pOldLayLeaf = pLayLeaf;
1654 pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
1656 if (pLayLeaf && pLayLeaf->IsInDocBody() && !bSameBody && !pLayLeaf->IsInFly() && pLayLeaf->IsInTab())
1658 // We found a next leaf in a next body frame, which is in an inline table. Make
1659 // sure we won't insert the follow anchor inside the table, but before it.
1660 SwTabFrame* pTabFrame = pLayLeaf->FindTabFrame();
1661 pLayLeaf = pTabFrame->GetUpper();
1664 continue;
1667 else
1669 // No candidate: insert a page and try again.
1670 if (eMakePage == MAKEPAGE_INSERT)
1672 InsertPage(FindPageFrame(), false);
1673 // If we already had a candidate, continue trying with that instead of starting from
1674 // scratch.
1675 pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
1676 continue;
1679 break;
1682 if( pLayLeaf )
1684 SwFlyAtContentFrame* pNew = nullptr;
1685 // Find the anchor frame to split.
1686 if (pFlyAnchor)
1688 // Split the anchor at char 0: all the content goes to the follow of the anchor.
1689 pFlyAnchor->SplitFrame(TextFrameIndex(0));
1690 auto pNext = static_cast<SwTextFrame*>(pFlyAnchor->GetNext());
1691 // The nesting case just splits the inner fly; the outer fly will split and move.
1692 if (!bNesting)
1694 // Move the new anchor frame, before the first child of pLayLeaf.
1695 pNext->MoveSubTree(pLayLeaf, pLayLeaf->Lower());
1698 // Now create the follow of the fly and anchor it in the master of the anchor.
1699 pNew = new SwFlyAtContentFrame(*pFly);
1700 while (pFlyAnchor->IsFollow())
1702 pFlyAnchor = pFlyAnchor->FindMaster();
1704 pFlyAnchor->AppendFly(pNew);
1706 pLayLeaf = pNew;
1708 return pLayLeaf;
1711 void SwRootFrame::DeleteEmptyFlys_()
1713 assert(mpFlyDestroy);
1715 while (!mpFlyDestroy->empty())
1717 SwFlyFrame* pFly = *mpFlyDestroy->begin();
1718 mpFlyDestroy->erase( mpFlyDestroy->begin() );
1719 // Allow deletion of non-empty flys: a fly with no content is still formatted to have a
1720 // height of MINLAY.
1721 if (!pFly->ContainsContent() && !pFly->IsDeleteForbidden())
1723 SwFrame::DestroyFrame(pFly);
1728 bool SwRootFrame::IsInFlyDelList( SwFlyFrame* pFly ) const
1730 if (!mpFlyDestroy)
1732 return false;
1735 return mpFlyDestroy->find(pFly) != mpFlyDestroy->end();
1738 const SwFlyAtContentFrame* SwFlyAtContentFrame::GetPrecede() const
1740 return static_cast<const SwFlyAtContentFrame*>(SwFlowFrame::GetPrecede());
1743 SwFlyAtContentFrame* SwFlyAtContentFrame::GetPrecede()
1745 return static_cast<SwFlyAtContentFrame*>(SwFlowFrame::GetPrecede());
1748 void SwFlyAtContentFrame::DelEmpty()
1750 SwTextFrame* pAnchor = FindAnchorCharFrame();
1751 if (pAnchor)
1753 if (SwFlowFrame* pAnchorPrecede = pAnchor->GetPrecede())
1755 // The anchor has a precede: invalidate it so that JoinFrame() is called on it.
1756 pAnchorPrecede->GetFrame().InvalidateSize();
1760 SwFlyAtContentFrame* pMaster = IsFollow() ? GetPrecede() : nullptr;
1761 if (pMaster)
1763 pMaster->SetFollow(GetFollow());
1766 SwFlyAtContentFrame* pFollow = GetFollow();
1767 if (pFollow)
1769 // I'll be deleted, so invalidate the position of my follow, so it can move up.
1770 pFollow->InvalidatePos();
1773 SetFollow(nullptr);
1776 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1777 aFrm.Height(0);
1779 InvalidateObjRectWithSpaces();
1781 if(getRootFrame())
1783 getRootFrame()->InsertEmptyFly(this);
1787 void SwRootFrame::InsertEmptyFly(SwFlyFrame* pDel)
1789 if (!mpFlyDestroy)
1791 mpFlyDestroy.reset(new SwFlyDestroyList);
1794 mpFlyDestroy->insert(pDel);
1797 SwLayoutFrame* SwFrame::GetPrevFlyLeaf()
1799 auto pFly = dynamic_cast<SwFlyAtContentFrame*>(FindFlyFrame());
1800 assert(pFly && "GetPrevFlyLeaf: missing fly frame");
1801 if (!pFly->IsFlySplitAllowed())
1803 return nullptr;
1806 return pFly->GetPrecede();
1809 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */