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 <dcontact.hxx>
21 #include <rootfrm.hxx>
22 #include <pagefrm.hxx>
23 #include <tocntntanchoredobjectposition.hxx>
24 #include <tolayoutanchoredobjectposition.hxx>
25 #include <frmtool.hxx>
26 #include <fmtornt.hxx>
27 #include <txtfrm.hxx>
28 #include <vector>
29 #include <svx/svdogrp.hxx>
30 #include <tools/fract.hxx>
31 #include <DocumentSettingManager.hxx>
32 #include <IDocumentState.hxx>
33 #include <txtfly.hxx>
34 #include <viewimp.hxx>
35 #include <textboxhelper.hxx>
36 #include <unomid.h>
37 #include <svx/svdoashp.hxx>
38 #include <osl/diagnose.h>
39 
40 using namespace ::com::sun::star;
41 
42 namespace {
43 
44 /// helper class for correct notification due to the positioning of
45 /// the anchored drawing object
46 class SwPosNotify
47 {
48     private:
49         SwAnchoredDrawObject* mpAnchoredDrawObj;
50         SwRect maOldObjRect;
51         SwPageFrame* mpOldPageFrame;
52 
53     public:
54         explicit SwPosNotify( SwAnchoredDrawObject* _pAnchoredDrawObj );
55         ~SwPosNotify() COVERITY_NOEXCEPT_FALSE;
56         // #i32795#
57         Point const & LastObjPos() const;
58 };
59 
60 }
61 
SwPosNotify(SwAnchoredDrawObject * _pAnchoredDrawObj)62 SwPosNotify::SwPosNotify( SwAnchoredDrawObject* _pAnchoredDrawObj ) :
63     mpAnchoredDrawObj( _pAnchoredDrawObj )
64 {
65     maOldObjRect = mpAnchoredDrawObj->GetObjRect();
66     // --> #i35640# - determine correct page frame
67     mpOldPageFrame = mpAnchoredDrawObj->GetPageFrame();
68 }
69 
~SwPosNotify()70 SwPosNotify::~SwPosNotify() COVERITY_NOEXCEPT_FALSE
71 {
72     if ( maOldObjRect != mpAnchoredDrawObj->GetObjRect() )
73     {
74         if( maOldObjRect.HasArea() && mpOldPageFrame )
75         {
76             mpAnchoredDrawObj->NotifyBackground( mpOldPageFrame, maOldObjRect,
77                                                  PrepareHint::FlyFrameLeave );
78         }
79         SwRect aNewObjRect( mpAnchoredDrawObj->GetObjRect() );
80         if( aNewObjRect.HasArea() )
81         {
82             // --> #i35640# - determine correct page frame
83             SwPageFrame* pNewPageFrame = mpAnchoredDrawObj->GetPageFrame();
84             if( pNewPageFrame )
85                 mpAnchoredDrawObj->NotifyBackground( pNewPageFrame, aNewObjRect,
86                                                      PrepareHint::FlyFrameArrive );
87         }
88 
89         ::ClrContourCache( mpAnchoredDrawObj->GetDrawObj() );
90 
91         // --> #i35640# - additional notify anchor text frame
92         // Needed for negative positioned drawing objects
93         // --> #i43255# - refine condition to avoid unneeded
94         // invalidations: anchored object had to be on the page of its anchor
95         // text frame.
96         if ( mpAnchoredDrawObj->GetAnchorFrame()->IsTextFrame() &&
97              mpOldPageFrame == mpAnchoredDrawObj->GetAnchorFrame()->FindPageFrame() )
98         {
99             mpAnchoredDrawObj->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
100         }
101 
102         // indicate a restart of the layout process
103         mpAnchoredDrawObj->SetRestartLayoutProcess( true );
104     }
105     else
106     {
107         // lock position
108         mpAnchoredDrawObj->LockPosition();
109 
110         if ( !mpAnchoredDrawObj->ConsiderForTextWrap() )
111         {
112             // indicate that object has to be considered for text wrap
113             mpAnchoredDrawObj->SetConsiderForTextWrap( true );
114             // invalidate 'background' in order to allow its 'background'
115             // to wrap around it.
116             mpAnchoredDrawObj->NotifyBackground( mpAnchoredDrawObj->GetPageFrame(),
117                                     mpAnchoredDrawObj->GetObjRectWithSpaces(),
118                                     PrepareHint::FlyFrameArrive );
119             // invalidate position of anchor frame in order to force
120             // a re-format of the anchor frame, which also causes a
121             // re-format of the invalid previous frames of the anchor frame.
122             mpAnchoredDrawObj->AnchorFrame()->InvalidatePos();
123         }
124     }
125     // tdf#101464 notify SwAccessibleMap about new drawing object position
126     if (mpOldPageFrame && mpOldPageFrame->getRootFrame()->IsAnyShellAccessible())
127     {
128         mpOldPageFrame->getRootFrame()->GetCurrShell()->Imp()->MoveAccessible(
129                 nullptr, mpAnchoredDrawObj->GetDrawObj(), maOldObjRect);
130     }
131 }
132 
133 // --> #i32795#
LastObjPos() const134 Point const & SwPosNotify::LastObjPos() const
135 {
136     return maOldObjRect.Pos();
137 }
138 
139 namespace {
140 
141 // #i32795#
142 /// helper class for oscillation control on object positioning
143 class SwObjPosOscillationControl
144 {
145     private:
146         const SwAnchoredDrawObject* mpAnchoredDrawObj;
147 
148         std::vector<Point> maObjPositions;
149 
150     public:
151         explicit SwObjPosOscillationControl( const SwAnchoredDrawObject& _rAnchoredDrawObj );
152 
153         bool OscillationDetected();
154 };
155 
156 }
157 
SwObjPosOscillationControl(const SwAnchoredDrawObject & _rAnchoredDrawObj)158 SwObjPosOscillationControl::SwObjPosOscillationControl(
159                                 const SwAnchoredDrawObject& _rAnchoredDrawObj )
160     : mpAnchoredDrawObj( &_rAnchoredDrawObj )
161 {
162 }
163 
OscillationDetected()164 bool SwObjPosOscillationControl::OscillationDetected()
165 {
166     bool bOscillationDetected = false;
167 
168     if ( maObjPositions.size() == 20 )
169     {
170         // position stack is full -> oscillation
171         bOscillationDetected = true;
172     }
173     else
174     {
175         Point aNewObjPos = mpAnchoredDrawObj->GetObjRect().Pos();
176         for ( auto const & pt : maObjPositions )
177         {
178             if ( aNewObjPos == pt )
179             {
180                 // position already occurred -> oscillation
181                 bOscillationDetected = true;
182                 break;
183             }
184         }
185         if ( !bOscillationDetected )
186         {
187             maObjPositions.push_back( aNewObjPos );
188         }
189     }
190 
191     return bOscillationDetected;
192 }
193 
194 
SwAnchoredDrawObject()195 SwAnchoredDrawObject::SwAnchoredDrawObject() :
196     SwAnchoredObject(),
197     mbValidPos( false ),
198     mbNotYetAttachedToAnchorFrame( true ),
199     // --> #i28749#
200     mbNotYetPositioned( true ),
201     // --> #i62875#
202     mbCaptureAfterLayoutDirChange( false )
203 {
204 }
205 
~SwAnchoredDrawObject()206 SwAnchoredDrawObject::~SwAnchoredDrawObject()
207 {
208 }
209 
210 // --> #i62875#
UpdateLayoutDir()211 void SwAnchoredDrawObject::UpdateLayoutDir()
212 {
213     SwFrameFormat::tLayoutDir nOldLayoutDir( GetFrameFormat().GetLayoutDir() );
214 
215     SwAnchoredObject::UpdateLayoutDir();
216 
217     if ( !NotYetPositioned() &&
218          GetFrameFormat().GetLayoutDir() != nOldLayoutDir &&
219          GetFrameFormat().GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) &&
220          !IsOutsidePage() )
221     {
222         mbCaptureAfterLayoutDirChange = true;
223     }
224 }
225 
226 // --> #i62875#
IsOutsidePage() const227 bool SwAnchoredDrawObject::IsOutsidePage() const
228 {
229     bool bOutsidePage( false );
230 
231     if ( !NotYetPositioned() && GetPageFrame() )
232     {
233         SwRect aTmpRect( GetObjRect() );
234         bOutsidePage =
235             ( aTmpRect.Intersection( GetPageFrame()->getFrameArea() ) != GetObjRect() );
236     }
237 
238     return bOutsidePage;
239 }
240 
MakeObjPos()241 void SwAnchoredDrawObject::MakeObjPos()
242 {
243     if ( IsPositioningInProgress() )
244     {
245         // nothing to do - positioning already in progress
246         return;
247     }
248 
249     if ( mbValidPos )
250     {
251         // nothing to do - position is valid
252         return;
253     }
254 
255     // --> #i28749# - anchored drawing object has to be attached
256     // to anchor frame
257     if ( mbNotYetAttachedToAnchorFrame )
258     {
259         OSL_FAIL( "<SwAnchoredDrawObject::MakeObjPos() - drawing object not yet attached to anchor frame -> no positioning" );
260         return;
261     }
262 
263     SwDrawContact* pDrawContact =
264                         static_cast<SwDrawContact*>(::GetUserCall( GetDrawObj() ));
265 
266     // --> #i28749# - if anchored drawing object hasn't been yet
267     // positioned, convert its positioning attributes, if its positioning
268     // attributes are given in horizontal left-to-right layout.
269     // --> #i36010# - Note: horizontal left-to-right layout is made
270     // the default layout direction for <SwDrawFrameFormat> instances. Thus, it has
271     // to be adjusted manually, if no adjustment of the positioning attributes
272     // have to be performed here.
273     // --> #i35635# - additionally move drawing object to the visible layer.
274     if ( mbNotYetPositioned )
275     {
276         // --> #i35635#
277         pDrawContact->MoveObjToVisibleLayer( DrawObj() );
278         // --> perform conversion of positioning
279         // attributes only for 'master' drawing objects
280         // #i44334#, #i44681# - check, if positioning
281         // attributes already have been set.
282         if ( dynamic_cast< const SwDrawVirtObj* >(GetDrawObj()) ==  nullptr &&
283              !static_cast<SwDrawFrameFormat&>(GetFrameFormat()).IsPosAttrSet() )
284         {
285             SetPositioningAttr();
286         }
287         // -->
288         // - reset internal flag after all needed actions are performed to
289         //   avoid callbacks from drawing layer
290         mbNotYetPositioned = false;
291     }
292 
293     // indicate that positioning is in progress
294     {
295         SwObjPositioningInProgress aObjPosInProgress( *this );
296 
297         // determine relative position of drawing object and set it
298         switch ( pDrawContact->GetAnchorId() )
299         {
300             case RndStdIds::FLY_AS_CHAR:
301             {
302                 // indicate that position will be valid after positioning is performed
303                 mbValidPos = true;
304                 // nothing to do, because as-character anchored objects are positioned
305                 // during the format of its anchor frame - see <SwFlyCntPortion::SetBase(..)>
306             }
307             break;
308             case RndStdIds::FLY_AT_PARA:
309             case RndStdIds::FLY_AT_CHAR:
310             {
311                 // --> #i32795# - move intrinsic positioning to
312                 // helper method <MakeObjPosAnchoredAtPara()>
313                 MakeObjPosAnchoredAtPara();
314             }
315             break;
316             case RndStdIds::FLY_AT_PAGE:
317             case RndStdIds::FLY_AT_FLY:
318             {
319                 // --> #i32795# - move intrinsic positioning to
320                 // helper method <MakeObjPosAnchoredAtLayout()>
321                 MakeObjPosAnchoredAtLayout();
322             }
323             break;
324             default:
325             {
326                 assert(!"<SwAnchoredDrawObject::MakeObjPos()> - unknown anchor type.");
327             }
328         }
329 
330         // keep, current object rectangle
331         // --> #i34748# - use new method <SetLastObjRect(..)>
332         SetLastObjRect( GetObjRect().SVRect() );
333 
334         // Assure for 'master' drawing object, that it's registered at the correct page.
335         // Perform check not for as-character anchored drawing objects and only if
336         // the anchor frame is valid.
337         if ( dynamic_cast< const SwDrawVirtObj* >(GetDrawObj()) ==  nullptr &&
338              !pDrawContact->ObjAnchoredAsChar() &&
339              GetAnchorFrame()->isFrameAreaDefinitionValid() )
340         {
341             pDrawContact->ChkPage();
342         }
343     }
344 
345     // --> #i62875#
346     if ( !(mbCaptureAfterLayoutDirChange &&
347          GetPageFrame()) )
348         return;
349 
350     SwRect aPageRect( GetPageFrame()->getFrameArea() );
351     SwRect aObjRect( GetObjRect() );
352     if ( aObjRect.Right() >= aPageRect.Right() + 10 )
353     {
354         Size aSize( aPageRect.Right() - aObjRect.Right(), 0 );
355         DrawObj()->Move( aSize );
356         aObjRect = GetObjRect();
357     }
358 
359     if ( aObjRect.Left() + 10 <= aPageRect.Left() )
360     {
361         Size aSize( aPageRect.Left() - aObjRect.Left(), 0 );
362         DrawObj()->Move( aSize );
363     }
364 
365     mbCaptureAfterLayoutDirChange = false;
366 }
367 
368 /** method for the intrinsic positioning of an at-paragraph|at-character
369     anchored drawing object
370 
371     #i32795# - helper method for method <MakeObjPos>
372 */
MakeObjPosAnchoredAtPara()373 void SwAnchoredDrawObject::MakeObjPosAnchoredAtPara()
374 {
375     // --> #i32795# - adopt positioning algorithm from Writer
376     // fly frames, which are anchored at paragraph|at character
377 
378     // Determine, if anchor frame can/has to be formatted.
379     // If yes, after each object positioning the anchor frame is formatted.
380     // If after the anchor frame format the object position isn't valid, the
381     // object is positioned again.
382     // --> #i43255# - refine condition: anchor frame format not
383     // allowed, if another anchored object, has to be consider its wrap influence
384     // --> #i50356# - format anchor frame containing the anchor
385     // position. E.g., for at-character anchored object this can be the follow
386     // frame of the anchor frame, which contains the anchor character.
387     bool bJoinLocked
388         = static_cast<const SwTextFrame*>(GetAnchorFrameContainingAnchPos())->IsAnyJoinLocked();
389     const bool bFormatAnchor = !bJoinLocked && !ConsiderObjWrapInfluenceOnObjPos()
390                                && !ConsiderObjWrapInfluenceOfOtherObjs();
391 
392     // Format of anchor is needed for (vertical) fly offsets, otherwise the
393     // lack of fly portions will result in an incorrect 0 offset.
394     bool bAddVerticalFlyOffsets = GetFrameFormat().getIDocumentSettingAccess().get(
395         DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
396     bool bFormatAnchorOnce = !bJoinLocked && bAddVerticalFlyOffsets;
397 
398     if (bFormatAnchor || bFormatAnchorOnce)
399     {
400         // --> #i50356#
401         GetAnchorFrameContainingAnchPos()->Calc(GetAnchorFrameContainingAnchPos()->getRootFrame()->GetCurrShell()->GetOut());
402     }
403 
404     bool bOscillationDetected = false;
405     SwObjPosOscillationControl aObjPosOscCtrl( *this );
406     // --> #i3317# - boolean, to apply temporarily the
407     // 'straightforward positioning process' for the frame due to its
408     // overlapping with a previous column.
409     bool bConsiderWrapInfluenceDueToOverlapPrevCol( false );
410     do {
411         // indicate that position will be valid after positioning is performed
412         mbValidPos = true;
413 
414         // --> #i35640# - correct scope for <SwPosNotify> instance
415         {
416             // create instance of <SwPosNotify> for correct notification
417             SwPosNotify aPosNotify( this );
418 
419             // determine and set position
420             objectpositioning::SwToContentAnchoredObjectPosition
421                     aObjPositioning( *DrawObj() );
422             aObjPositioning.CalcPosition();
423 
424             // get further needed results of the positioning algorithm
425             SetVertPosOrientFrame ( aObjPositioning.GetVertPosOrientFrame() );
426             SetDrawObjAnchor();
427 
428             // check for object position oscillation, if position has changed.
429             if ( GetObjRect().Pos() != aPosNotify.LastObjPos() )
430             {
431                 bOscillationDetected = aObjPosOscCtrl.OscillationDetected();
432             }
433         }
434         // format anchor frame, if requested.
435         // Note: the format of the anchor frame can cause the object position
436         // to be invalid.
437         if ( bFormatAnchor )
438         {
439             // --> #i50356#
440             GetAnchorFrameContainingAnchPos()->Calc(GetAnchorFrameContainingAnchPos()->getRootFrame()->GetCurrShell()->GetOut());
441         }
442 
443         // --> #i3317#
444         if ( !ConsiderObjWrapInfluenceOnObjPos() &&
445              OverlapsPrevColumn() )
446         {
447             bConsiderWrapInfluenceDueToOverlapPrevCol = true;
448         }
449     } while ( !mbValidPos && !bOscillationDetected &&
450               !bConsiderWrapInfluenceDueToOverlapPrevCol );
451 
452     // --> #i3317# - consider a detected oscillation and overlapping
453     // with previous column.
454     // temporarily consider the anchored objects wrapping style influence
455     if ( bOscillationDetected || bConsiderWrapInfluenceDueToOverlapPrevCol )
456     {
457         SetTmpConsiderWrapInfluence( true );
458         SetRestartLayoutProcess( true );
459     }
460 }
461 
462 /** method for the intrinsic positioning of an at-page|at-frame anchored
463     drawing object
464 
465     #i32795# - helper method for method <MakeObjPos>
466 */
MakeObjPosAnchoredAtLayout()467 void SwAnchoredDrawObject::MakeObjPosAnchoredAtLayout()
468 {
469     // indicate that position will be valid after positioning is performed
470     mbValidPos = true;
471 
472     // create instance of <SwPosNotify> for correct notification
473     SwPosNotify aPosNotify( this );
474 
475     // determine position
476     objectpositioning::SwToLayoutAnchoredObjectPosition
477             aObjPositioning( *DrawObj() );
478     aObjPositioning.CalcPosition();
479 
480     // set position
481 
482     // --> #i31698#
483     // --> #i34995# - setting anchor position needed for filters,
484     // especially for the xml-filter to the OpenOffice.org file format
485     {
486         const Point aNewAnchorPos =
487                     GetAnchorFrame()->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
488         DrawObj()->SetAnchorPos( aNewAnchorPos );
489         // --> #i70122# - missing invalidation
490         InvalidateObjRectWithSpaces();
491     }
492     SetCurrRelPos( aObjPositioning.GetRelPos() );
493     const SwFrame* pAnchorFrame = GetAnchorFrame();
494     SwRectFnSet aRectFnSet(pAnchorFrame);
495     const Point aAnchPos( aRectFnSet.GetPos(pAnchorFrame->getFrameArea()) );
496     SetObjLeft( aAnchPos.X() + GetCurrRelPos().X() );
497     SetObjTop( aAnchPos.Y() + GetCurrRelPos().Y() );
498 }
499 
SetDrawObjAnchor()500 void SwAnchoredDrawObject::SetDrawObjAnchor()
501 {
502     // new anchor position
503     // --> #i31698# -
504     Point aNewAnchorPos =
505                 GetAnchorFrame()->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
506     Point aCurrAnchorPos = GetDrawObj()->GetAnchorPos();
507     if ( aNewAnchorPos != aCurrAnchorPos )
508     {
509         // determine movement to be applied after setting the new anchor position
510         Size aMove( aCurrAnchorPos.getX() - aNewAnchorPos.getX(),
511                     aCurrAnchorPos.getY() - aNewAnchorPos.getY() );
512         // set new anchor position
513         DrawObj()->SetAnchorPos( aNewAnchorPos );
514         // correct object position, caused by setting new anchor position
515         DrawObj()->Move( aMove );
516         // --> #i70122# - missing invalidation
517         InvalidateObjRectWithSpaces();
518     }
519 }
520 
521 /** method to invalidate the given page frame
522 
523     #i28701#
524 */
InvalidatePage_(SwPageFrame * _pPageFrame)525 void SwAnchoredDrawObject::InvalidatePage_( SwPageFrame* _pPageFrame )
526 {
527     if ( !_pPageFrame || _pPageFrame->GetFormat()->GetDoc()->IsInDtor() )
528         return;
529 
530     if ( !_pPageFrame->GetUpper() )
531         return;
532 
533     // --> #i35007# - correct invalidation for as-character
534     // anchored objects.
535     if ( GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR )
536     {
537         _pPageFrame->InvalidateFlyInCnt();
538     }
539     else
540     {
541         _pPageFrame->InvalidateFlyLayout();
542     }
543 
544     SwRootFrame* pRootFrame = static_cast<SwRootFrame*>(_pPageFrame->GetUpper());
545     pRootFrame->DisallowTurbo();
546     if ( pRootFrame->GetTurbo() )
547     {
548         const SwContentFrame* pTmpFrame = pRootFrame->GetTurbo();
549         pRootFrame->ResetTurbo();
550         pTmpFrame->InvalidatePage();
551     }
552     pRootFrame->SetIdleFlags();
553 }
554 
InvalidateObjPos()555 void SwAnchoredDrawObject::InvalidateObjPos()
556 {
557     // --> #i28701# - check, if invalidation is allowed
558     if ( !(mbValidPos &&
559          InvalidationOfPosAllowed()) )
560         return;
561 
562     mbValidPos = false;
563     // --> #i68520#
564     InvalidateObjRectWithSpaces();
565 
566     // --> #i44339# - check, if anchor frame exists.
567     if ( !GetAnchorFrame() )
568         return;
569 
570     // --> #118547# - notify anchor frame of as-character
571     // anchored object, because its positioned by the format of its anchor frame.
572     // --> #i44559# - assure, that text hint is already
573     // existing in the text frame
574     if ( dynamic_cast< const SwTextFrame* >(GetAnchorFrame()) !=  nullptr &&
575          (GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) )
576     {
577         SwTextFrame* pAnchorTextFrame( static_cast<SwTextFrame*>(AnchorFrame()) );
578         if (pAnchorTextFrame->CalcFlyPos(&GetFrameFormat()) != TextFrameIndex(COMPLETE_STRING))
579         {
580             AnchorFrame()->Prepare( PrepareHint::FlyFrameAttributesChanged, &GetFrameFormat() );
581         }
582     }
583 
584     SwPageFrame* pPageFrame = AnchorFrame()->FindPageFrame();
585     InvalidatePage_( pPageFrame );
586 
587     // --> #i32270# - also invalidate page frame, at which the
588     // drawing object is registered at.
589     SwPageFrame* pPageFrameRegisteredAt = GetPageFrame();
590     if ( pPageFrameRegisteredAt &&
591          pPageFrameRegisteredAt != pPageFrame )
592     {
593         InvalidatePage_( pPageFrameRegisteredAt );
594     }
595     // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
596     // is replaced by method <FindPageFrameOfAnchor()>. It's return value
597     // have to be checked.
598     SwPageFrame* pPageFrameOfAnchor = FindPageFrameOfAnchor();
599     if ( pPageFrameOfAnchor &&
600          pPageFrameOfAnchor != pPageFrame &&
601          pPageFrameOfAnchor != pPageFrameRegisteredAt )
602     {
603         InvalidatePage_( pPageFrameOfAnchor );
604     }
605 }
606 
GetFrameFormat()607 SwFrameFormat& SwAnchoredDrawObject::GetFrameFormat()
608 {
609     assert(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
610     return *(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
611 }
GetFrameFormat() const612 const SwFrameFormat& SwAnchoredDrawObject::GetFrameFormat() const
613 {
614     assert(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
615     return *(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
616 }
617 
GetObjRect() const618 SwRect SwAnchoredDrawObject::GetObjRect() const
619 {
620     // use geometry of drawing object
621     //return GetDrawObj()->GetCurrentBoundRect();
622     return SwRect(GetDrawObj()->GetSnapRect());
623 }
624 
625 namespace
626 {
627     // Imagine an open book, inside margin is the one that is at the inner side of the pages, at the center of the book,
628     // outside margin is at the two opposite edges of the book.
629     // outside --text-- inside | inside --text-- outside
630     // With mirrored margins, when relating the size of an object from the inside margin for example, on the
631     // first page we calculate the new size of the object using the size of the right margin,
632     // on second page the left margin, third page right margin, etc.
getInsideOutsideRelativeWidth(bool isOutside,const SwPageFrame * const pPageFrame)633     tools::Long getInsideOutsideRelativeWidth(bool isOutside, const SwPageFrame* const pPageFrame)
634     {
635         // Alternating between the only two possible cases: inside and outside.
636         // Inside = false, Outside = true.
637         auto nPageNum = pPageFrame->GetPhyPageNum();
638         if (nPageNum % 2 == (isOutside ? 0 : 1))
639             return pPageFrame->GetRightMargin();
640         else
641             return pPageFrame->GetLeftMargin();
642     }
643 }
644 
645 // --> #i70122#
GetObjBoundRect() const646 SwRect SwAnchoredDrawObject::GetObjBoundRect() const
647 {
648     bool bGroupShape = dynamic_cast<const SdrObjGroup*>( GetDrawObj() );
649     // Resize objects with relative width or height
650     if ( !bGroupShape && GetPageFrame( ) && ( GetDrawObj( )->GetRelativeWidth( ) || GetDrawObj()->GetRelativeHeight( ) ) )
651     {
652         tools::Rectangle aCurrObjRect = GetDrawObj()->GetCurrentBoundRect();
653 
654         tools::Long nTargetWidth = aCurrObjRect.GetWidth( );
655         if ( GetDrawObj( )->GetRelativeWidth( ) )
656         {
657             tools::Long nWidth = 0;
658             if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::FRAME)
659                 // Exclude margins.
660                 nWidth = GetPageFrame()->getFramePrintArea().SVRect().GetWidth();
661             // Here we handle the relative size of the width of some shape.
662             // The size of the shape's width is going to be relative to the size of the left margin.
663             // E.g.: (left margin = 8 && relative size = 150%) -> width of some shape = 12.
664             else if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::PAGE_LEFT)
665             {
666                 if (GetPageFrame()->GetPageDesc()->GetUseOn() == UseOnPage::Mirror)
667                     // We want to get the width of whatever is going through here using the size of the
668                     // outside margin.
669                     nWidth = getInsideOutsideRelativeWidth(true, GetPageFrame());
670                 else
671                     nWidth = GetPageFrame()->GetLeftMargin();
672             }
673             // Same as the left margin above.
674             else if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::PAGE_RIGHT)
675                 if (GetPageFrame()->GetPageDesc()->GetUseOn() == UseOnPage::Mirror)
676                     // We want to get the width of whatever is going through here using the size of the
677                     // inside margin.
678                     nWidth = getInsideOutsideRelativeWidth(false, GetPageFrame());
679                 else
680                     nWidth = GetPageFrame()->GetRightMargin();
681             else
682                 nWidth = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect().GetWidth();
683             nTargetWidth = nWidth * (*GetDrawObj( )->GetRelativeWidth());
684         }
685 
686         bool bCheck = GetDrawObj()->GetRelativeHeight();
687         if (bCheck)
688         {
689             auto pObjCustomShape = dynamic_cast<const SdrObjCustomShape*>(GetDrawObj());
690             bCheck = !pObjCustomShape || !pObjCustomShape->IsAutoGrowHeight();
691         }
692 
693         tools::Long nTargetHeight = aCurrObjRect.GetHeight();
694         if (bCheck)
695         {
696             tools::Long nHeight = 0;
697             if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::FRAME)
698                 // Exclude margins.
699                 nHeight = GetPageFrame()->getFramePrintArea().SVRect().GetHeight();
700             else if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::PAGE_PRINT_AREA)
701             {
702                 // count required height: print area top = top margin + header
703                 SwRect aHeaderRect;
704                 const SwHeaderFrame* pHeaderFrame = GetPageFrame()->GetHeaderFrame();
705                 if (pHeaderFrame)
706                     aHeaderRect = pHeaderFrame->GetPaintArea();
707                 nHeight = GetPageFrame()->GetTopMargin() + aHeaderRect.Height();
708             }
709             else if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
710             {
711                 // count required height: print area bottom = bottom margin + footer
712                 SwRect aFooterRect;
713                 auto pFooterFrame = GetPageFrame()->GetFooterFrame();
714                 if (pFooterFrame)
715                     aFooterRect = pFooterFrame->GetPaintArea();
716                 nHeight = GetPageFrame()->GetBottomMargin() + aFooterRect.Height();
717             }
718             else
719                 nHeight = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect().GetHeight();
720             nTargetHeight = nHeight * (*GetDrawObj()->GetRelativeHeight());
721         }
722 
723         if ( nTargetWidth != aCurrObjRect.GetWidth( ) || nTargetHeight != aCurrObjRect.GetHeight( ) )
724         {
725             SwDoc* pDoc = const_cast<SwDoc*>(GetPageFrame()->GetFormat()->GetDoc());
726 
727             bool bEnableSetModified = pDoc->getIDocumentState().IsEnableSetModified();
728             pDoc->getIDocumentState().SetEnableSetModified(false);
729             auto pObject = const_cast<SdrObject*>(GetDrawObj());
730             pObject->Resize( aCurrObjRect.TopLeft(),
731                     Fraction( nTargetWidth, aCurrObjRect.GetWidth() ),
732                     Fraction( nTargetHeight, aCurrObjRect.GetHeight() ), false );
733 
734             if (SwFrameFormat* pFrameFormat = FindFrameFormat(pObject))
735             {
736                 if (SwTextBoxHelper::isTextBox(pFrameFormat, RES_DRAWFRMFMT))
737                 {
738                     // Shape has relative size and also a textbox, update its text area as well.
739                     uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
740                     SwTextBoxHelper::syncProperty(pFrameFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE,
741                                                   uno::makeAny(xShape->getSize()));
742                 }
743             }
744 
745             pDoc->getIDocumentState().SetEnableSetModified(bEnableSetModified);
746         }
747     }
748     return SwRect(GetDrawObj()->GetCurrentBoundRect());
749 }
750 
751 // --> #i68520#
SetObjTop_(const SwTwips _nTop)752 bool SwAnchoredDrawObject::SetObjTop_( const SwTwips _nTop )
753 {
754     SwTwips nDiff = _nTop - GetObjRect().Top();
755     DrawObj()->Move( Size( 0, nDiff ) );
756 
757     return nDiff != 0;
758 }
SetObjLeft_(const SwTwips _nLeft)759 bool SwAnchoredDrawObject::SetObjLeft_( const SwTwips _nLeft )
760 {
761     SwTwips nDiff = _nLeft - GetObjRect().Left();
762     DrawObj()->Move( Size( nDiff, 0 ) );
763 
764     return nDiff != 0;
765 }
766 
767 /** adjust positioning and alignment attributes for new anchor frame
768 
769     #i33313# - add second optional parameter <_pNewObjRect>
770 */
AdjustPositioningAttr(const SwFrame * _pNewAnchorFrame,const SwRect * _pNewObjRect)771 void SwAnchoredDrawObject::AdjustPositioningAttr( const SwFrame* _pNewAnchorFrame,
772                                                   const SwRect* _pNewObjRect )
773 {
774     SwTwips nHoriRelPos = 0;
775     SwTwips nVertRelPos = 0;
776     const Point aAnchorPos = _pNewAnchorFrame->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
777     // --> #i33313#
778     const SwRect aObjRect( _pNewObjRect ? *_pNewObjRect : GetObjRect() );
779     const bool bVert = _pNewAnchorFrame->IsVertical();
780     const bool bR2L = _pNewAnchorFrame->IsRightToLeft();
781     if ( bVert )
782     {
783         nHoriRelPos = aObjRect.Top() - aAnchorPos.Y();
784         nVertRelPos = aAnchorPos.X() - aObjRect.Right();
785     }
786     else if ( bR2L )
787     {
788         nHoriRelPos = aAnchorPos.X() - aObjRect.Right();
789         nVertRelPos = aObjRect.Top() - aAnchorPos.Y();
790     }
791     else
792     {
793         nHoriRelPos = aObjRect.Left() - aAnchorPos.X();
794         nVertRelPos = aObjRect.Top() - aAnchorPos.Y();
795     }
796 
797     SwFormatHoriOrient hori(nHoriRelPos, text::HoriOrientation::NONE, text::RelOrientation::FRAME);
798     SwFormatVertOrient vert(nVertRelPos, text::VertOrientation::NONE, text::RelOrientation::FRAME);
799     SfxItemSet items(GetFrameFormat().GetDoc()->GetAttrPool(), svl::Items<RES_VERT_ORIENT, RES_HORI_ORIENT>());
800     items.Put(hori);
801     items.Put(vert);
802     GetFrameFormat().GetDoc()->SetAttr(items, GetFrameFormat());
803 }
804 
805 // --> #i34748# - change return type.
806 // If member <mpLastObjRect> is NULL, create one.
SetLastObjRect(const tools::Rectangle & _rNewLastRect)807 void SwAnchoredDrawObject::SetLastObjRect( const tools::Rectangle& _rNewLastRect )
808 {
809     maLastObjRect = _rNewLastRect;
810 }
811 
ObjectAttachedToAnchorFrame()812 void SwAnchoredDrawObject::ObjectAttachedToAnchorFrame()
813 {
814     // --> #i31698#
815     SwAnchoredObject::ObjectAttachedToAnchorFrame();
816 
817     if ( mbNotYetAttachedToAnchorFrame )
818     {
819         mbNotYetAttachedToAnchorFrame = false;
820     }
821 }
822 
823 /** method to set positioning attributes
824 
825     #i35798#
826     During load the positioning attributes aren't set.
827     Thus, the positioning attributes are set by the current object geometry.
828     This method is also used for the conversion for drawing objects
829     (not anchored as-character) imported from OpenOffice.org file format
830     once and directly before the first positioning.
831 */
SetPositioningAttr()832 void SwAnchoredDrawObject::SetPositioningAttr()
833 {
834     SwDrawContact* pDrawContact =
835                         static_cast<SwDrawContact*>(GetUserCall( GetDrawObj() ));
836 
837     if ( !pDrawContact->ObjAnchoredAsChar() )
838     {
839         SwRect aObjRect( GetObjRect() );
840 
841         SwTwips nHoriPos = aObjRect.Left();
842         SwTwips nVertPos = aObjRect.Top();
843         // #i44334#, #i44681#
844         // perform conversion only if position is in horizontal-left-to-right-layout.
845         if ( GetFrameFormat().GetPositionLayoutDir() ==
846                 text::PositionLayoutDir::PositionInHoriL2R )
847         {
848             SwFrameFormat::tLayoutDir eLayoutDir = GetFrameFormat().GetLayoutDir();
849             switch ( eLayoutDir )
850             {
851                 case SwFrameFormat::HORI_L2R:
852                 {
853                     // nothing to do
854                 }
855                 break;
856                 case SwFrameFormat::HORI_R2L:
857                 {
858                     nHoriPos = -aObjRect.Left() - aObjRect.Width();
859                 }
860                 break;
861                 case SwFrameFormat::VERT_R2L:
862                 {
863                     nHoriPos = aObjRect.Top();
864                     nVertPos = -aObjRect.Left() - aObjRect.Width();
865                 }
866                 break;
867                 default:
868                 {
869                     assert(!"<SwAnchoredDrawObject::SetPositioningAttr()> - unsupported layout direction");
870                 }
871             }
872         }
873 
874         // --> #i71182#
875         // only change position - do not lose other attributes
876 
877         SwFormatHoriOrient aHori( GetFrameFormat().GetHoriOrient() );
878         if (nHoriPos != aHori.GetPos()) {
879             aHori.SetPos( nHoriPos );
880             InvalidateObjRectWithSpaces();
881             GetFrameFormat().SetFormatAttr( aHori );
882         }
883 
884         SwFormatVertOrient aVert( GetFrameFormat().GetVertOrient() );
885         if (nVertPos != aVert.GetPos()) {
886             aVert.SetPos( nVertPos );
887             InvalidateObjRectWithSpaces();
888             GetFrameFormat().SetFormatAttr( aVert );
889         }
890 
891         // --> #i36010# - set layout direction of the position
892         GetFrameFormat().SetPositionLayoutDir(
893             text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
894     }
895     // --> #i65798# - also for as-character anchored objects
896     // --> #i45952# - indicate that position
897     // attributes are set now.
898     static_cast<SwDrawFrameFormat&>(GetFrameFormat()).PosAttrSet();
899 }
900 
NotifyBackground(SwPageFrame * _pPageFrame,const SwRect & _rRect,PrepareHint _eHint)901 void SwAnchoredDrawObject::NotifyBackground( SwPageFrame* _pPageFrame,
902                                              const SwRect& _rRect,
903                                              PrepareHint _eHint )
904 {
905     ::Notify_Background( GetDrawObj(), _pPageFrame, _rRect, _eHint, true );
906 }
907 
908 /** method to assure that anchored object is registered at the correct
909     page frame
910 
911     #i28701#
912 */
RegisterAtCorrectPage()913 void SwAnchoredDrawObject::RegisterAtCorrectPage()
914 {
915     SwPageFrame* pPageFrame( nullptr );
916     if ( GetVertPosOrientFrame() )
917     {
918         pPageFrame = const_cast<SwPageFrame*>(GetVertPosOrientFrame()->FindPageFrame());
919     }
920     if ( pPageFrame && GetPageFrame() != pPageFrame )
921     {
922         RegisterAtPage(*pPageFrame);
923     }
924 }
925 
RegisterAtPage(SwPageFrame & rPageFrame)926 void SwAnchoredDrawObject::RegisterAtPage(SwPageFrame & rPageFrame)
927 {
928     assert(GetPageFrame() != &rPageFrame);
929     if (GetPageFrame())
930     {
931         GetPageFrame()->RemoveDrawObjFromPage( *this );
932     }
933     rPageFrame.AppendDrawObjToPage( *this );
934 }
935 
936 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
937