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