1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <tools/diagnose_ex.h>
22 #include <osl/diagnose.hxx>
23 #include <sal/log.hxx>
24 #include <com/sun/star/awt/Rectangle.hpp>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/awt/FontWeight.hpp>
27 #include <comphelper/anytostring.hxx>
28 #include <cppuhelper/exc_hlp.hxx>
30 #include <vcl/metaact.hxx>
31 #include <vcl/gdimtf.hxx>
32 #include <vcl/graph.hxx>
34 #include <basegfx/numeric/ftools.hxx>
36 #include <rtl/math.hxx>
38 #include <com/sun/star/drawing/TextAnimationKind.hpp>
40 #include <vcl/svapp.hxx>
41 #include <vcl/window.hxx>
42 #include <tools/stream.hxx>
43 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
44 #include <com/sun/star/datatransfer/XTransferable.hpp>
46 #include <comphelper/scopeguard.hxx>
47 #include <canvas/canvastools.hxx>
55 #include "drawshapesubsetting.hxx"
56 #include "drawshape.hxx"
57 #include <eventqueue.hxx>
58 #include <wakeupevent.hxx>
59 #include <subsettableshapemanager.hxx>
60 #include "intrinsicanimationactivity.hxx"
61 #include <slideshowexceptions.hxx>
63 #include "gdimtftools.hxx"
64 #include "drawinglayeranimation.hxx"
68 using namespace ::com::sun::star
;
80 GDIMetaFileSharedPtr
const & DrawShape::forceScrollTextMetaFile()
82 if ((mnCurrMtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != MTF_LOAD_SCROLL_TEXT_MTF
)
84 // reload with added flags:
85 mnCurrMtfLoadFlags
|= MTF_LOAD_SCROLL_TEXT_MTF
;
86 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(mxShape
, uno::UNO_QUERY
),
87 mxPage
, mnCurrMtfLoadFlags
,
91 mpCurrMtf
.reset( new GDIMetaFile
);
93 // TODO(F1): Currently, the scroll metafile will
94 // never contain any verbose text comments. Thus,
95 // can only display the full mtf content, no
97 maSubsetting
.reset( mpCurrMtf
);
99 // adapt maBounds. the requested scroll text metafile
100 // will typically have dimension different from the
102 ::basegfx::B2DRectangle aScrollRect
, aPaintRect
;
103 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect
,
106 "DrawShape::forceScrollTextMetaFile(): Could "
107 "not extract scroll anim rectangles from mtf" );
109 // take the larger one of the two rectangles (that
110 // should be the bound rect of the retrieved
112 if( aScrollRect
.isInside( aPaintRect
) )
113 maBounds
= aScrollRect
;
115 maBounds
= aPaintRect
;
120 void DrawShape::updateStateIds() const
122 // Update the states, we've just redrawn or created a new
124 if( mpAttributeLayer
)
126 mnAttributeTransformationState
= mpAttributeLayer
->getTransformationState();
127 mnAttributeClipState
= mpAttributeLayer
->getClipState();
128 mnAttributeAlphaState
= mpAttributeLayer
->getAlphaState();
129 mnAttributePositionState
= mpAttributeLayer
->getPositionState();
130 mnAttributeContentState
= mpAttributeLayer
->getContentState();
131 mnAttributeVisibilityState
= mpAttributeLayer
->getVisibilityState();
135 ViewShape::RenderArgs
DrawShape::getViewRenderArgs() const
137 return ViewShape::RenderArgs(
141 getActualUnitShapeBounds(),
143 maSubsetting
.getActiveSubsets(),
147 bool DrawShape::implRender( UpdateFlags nUpdateFlags
) const
149 SAL_INFO( "slideshow", "::presentation::internal::DrawShape::implRender()" );
150 SAL_INFO( "slideshow", "::presentation::internal::DrawShape: 0x" << std::hex
<< this );
152 // will perform the update now, clear update-enforcing
154 mbForceUpdate
= false;
155 mbAttributeLayerRevoked
= false;
157 ENSURE_OR_RETURN_FALSE( !maViewShapes
.empty(),
158 "DrawShape::implRender(): render called on DrawShape without views" );
160 if( maBounds
.isEmpty() )
162 // zero-sized shapes are effectively invisible,
163 // thus, we save us the rendering...
167 // redraw all view shapes, by calling their update() method
168 ViewShape::RenderArgs
renderArgs( getViewRenderArgs() );
169 bool bVisible
= isVisible();
170 if( ::std::count_if( maViewShapes
.begin(),
172 [this, &bVisible
, &renderArgs
, &nUpdateFlags
]
173 ( const ViewShapeSharedPtr
& pShape
)
174 { return pShape
->update( this->mpCurrMtf
,
178 != static_cast<ViewShapeVector::difference_type
>(maViewShapes
.size()) )
180 // at least one of the ViewShape::update() calls did return
181 // false - update failed on at least one ViewLayer
185 // successfully redrawn - update state IDs to detect next changes
191 UpdateFlags
DrawShape::getUpdateFlags() const
193 // default: update nothing, unless ShapeAttributeStack
194 // tells us below, or if the attribute layer was revoked
195 UpdateFlags
nUpdateFlags(UpdateFlags::NONE
);
197 // possibly the whole shape content changed
198 if( mbAttributeLayerRevoked
)
199 nUpdateFlags
= UpdateFlags::Content
;
202 // determine what has to be updated
205 // do we have an attribute layer?
206 if( mpAttributeLayer
)
208 // Prevent nUpdateFlags to be modified when the shape is not
209 // visible, except when it just was hidden.
210 if (mpAttributeLayer
->getVisibility()
211 || mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
213 if (mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
215 // Change of the visibility state is mapped to
216 // content change because when the visibility
217 // changes then usually a sprite is shown or hidden
218 // and the background under has to be painted once.
219 nUpdateFlags
|= UpdateFlags::Content
;
222 // TODO(P1): This can be done without conditional branching.
224 if( mpAttributeLayer
->getPositionState() != mnAttributePositionState
)
226 nUpdateFlags
|= UpdateFlags::Position
;
228 if( mpAttributeLayer
->getAlphaState() != mnAttributeAlphaState
)
230 nUpdateFlags
|= UpdateFlags::Alpha
;
232 if( mpAttributeLayer
->getClipState() != mnAttributeClipState
)
234 nUpdateFlags
|= UpdateFlags::Clip
;
236 if( mpAttributeLayer
->getTransformationState() != mnAttributeTransformationState
)
238 nUpdateFlags
|= UpdateFlags::Transformation
;
240 if( mpAttributeLayer
->getContentState() != mnAttributeContentState
)
242 nUpdateFlags
|= UpdateFlags::Content
;
250 ::basegfx::B2DRectangle
DrawShape::getActualUnitShapeBounds() const
252 ENSURE_OR_THROW( !maViewShapes
.empty(),
253 "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
255 const VectorOfDocTreeNodes
& rSubsets(
256 maSubsetting
.getActiveSubsets() );
258 const ::basegfx::B2DRectangle
aDefaultBounds( 0.0,0.0,1.0,1.0 );
260 // perform the cheapest check first
261 if( rSubsets
.empty() )
263 // if subset contains the whole shape, no need to call
264 // the somewhat expensive bound calculation, since as
265 // long as the subset is empty, this branch will be
267 return aDefaultBounds
;
271 OSL_ENSURE( rSubsets
.size() != 1 ||
272 !rSubsets
.front().isEmpty(),
273 "DrawShape::getActualUnitShapeBounds() expects a "
274 "_non-empty_ subset vector for a subsetted shape!" );
276 // are the cached bounds still valid?
277 if( !maCurrentShapeUnitBounds
)
279 // no, (re)generate them
280 // =====================
282 // setup cached values to defaults (might fail to
283 // retrieve true bounds below)
284 maCurrentShapeUnitBounds
.reset( aDefaultBounds
);
286 // TODO(P2): the subset of the master shape (that from
287 // which the subsets are subtracted) changes
288 // relatively often (every time a subset shape is
289 // added or removed). Maybe we should exclude it here,
290 // always assuming full bounds?
292 ::cppcanvas::CanvasSharedPtr
pDestinationCanvas(
293 maViewShapes
.front()->getViewLayer()->getCanvas() );
295 // TODO(Q2): Although this _is_ currently
296 // view-agnostic, it might not stay like
297 // that. Maybe this method should again be moved
299 ::cppcanvas::RendererSharedPtr
pRenderer(
300 maViewShapes
.front()->getRenderer(
301 pDestinationCanvas
, mpCurrMtf
, mpAttributeLayer
) );
303 // If we cannot not prefetch, be defensive and assume
307 // temporarily, switch total transformation to identity
308 // (need the bounds in the [0,1]x[0,1] unit coordinate
310 ::basegfx::B2DHomMatrix aEmptyTransformation
;
312 ::basegfx::B2DHomMatrix
aOldTransform( pDestinationCanvas
->getTransformation() );
313 pDestinationCanvas
->setTransformation( aEmptyTransformation
);
314 pRenderer
->setTransformation( aEmptyTransformation
);
316 // restore old transformation when leaving the scope
317 const ::comphelper::ScopeGuard
aGuard(
318 [&pDestinationCanvas
, &aOldTransform
]()
319 { return pDestinationCanvas
->setTransformation( aOldTransform
); } );
322 // retrieve bounds for subset of whole metafile
325 ::basegfx::B2DRange aTotalBounds
;
327 // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
329 for( const auto& rDocTreeNode
: rSubsets
)
330 aTotalBounds
.expand( pRenderer
->getSubsetArea(
331 rDocTreeNode
.getStartIndex(),
332 rDocTreeNode
.getEndIndex() ) );
334 OSL_ENSURE( aTotalBounds
.getMinX() >= -0.1 &&
335 aTotalBounds
.getMinY() >= -0.1 &&
336 aTotalBounds
.getMaxX() <= 1.1 &&
337 aTotalBounds
.getMaxY() <= 1.1,
338 "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
340 // really make sure no shape appears larger than its
341 // original bounds (there _are_ some pathologic cases,
342 // especially when imported from PPT, that have
343 // e.g. obscenely large polygon bounds)
344 aTotalBounds
.intersect(
345 ::basegfx::B2DRange( 0.0, 0.0,
348 maCurrentShapeUnitBounds
.reset( aTotalBounds
);
352 return *maCurrentShapeUnitBounds
;
356 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
357 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
360 const SlideShowContext
& rContext
) :
362 mxPage( xContainingPage
),
363 maAnimationFrames(), // empty, we don't have no intrinsic animation
366 mnCurrMtfLoadFlags( bForeignSource
367 ? MTF_LOAD_FOREIGN_SOURCE
: MTF_LOAD_NONE
),
368 maCurrentShapeUnitBounds(),
369 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
370 maBounds( getAPIShapeBounds( xShape
) ),
372 mpIntrinsicAnimationActivity(),
373 mnAttributeTransformationState(0),
374 mnAttributeClipState(0),
375 mnAttributeAlphaState(0),
376 mnAttributePositionState(0),
377 mnAttributeContentState(0),
378 mnAttributeVisibilityState(0),
380 mxComponentContext( rContext
.mxComponentContext
),
381 maHyperlinkIndices(),
382 maHyperlinkRegions(),
384 mnIsAnimatedCount(0),
385 mnAnimationLoopCount(0),
387 mbForceUpdate( false ),
388 mbAttributeLayerRevoked( false ),
389 mbDrawingLayerAnim( false )
391 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
392 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
394 // check for drawing layer animations:
395 drawing::TextAnimationKind eKind
= drawing::TextAnimationKind_NONE
;
396 uno::Reference
<beans::XPropertySet
> xPropSet( mxShape
,
399 getPropertyValue( eKind
, xPropSet
,
400 "TextAnimationKind" );
401 mbDrawingLayerAnim
= (eKind
!= drawing::TextAnimationKind_NONE
);
403 // must NOT be called from within initializer list, uses
404 // state from mnCurrMtfLoadFlags!
405 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY
),
406 xContainingPage
, mnCurrMtfLoadFlags
,
407 mxComponentContext
);
409 mpCurrMtf
.reset(new GDIMetaFile
);
411 maSubsetting
.reset( mpCurrMtf
);
413 prepareHyperlinkIndices();
416 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
417 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
419 const Graphic
& rGraphic
,
420 const SlideShowContext
& rContext
) :
422 mxPage( xContainingPage
),
426 mnCurrMtfLoadFlags( MTF_LOAD_NONE
),
427 maCurrentShapeUnitBounds(),
428 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
429 maBounds( getAPIShapeBounds( xShape
) ),
431 mpIntrinsicAnimationActivity(),
432 mnAttributeTransformationState(0),
433 mnAttributeClipState(0),
434 mnAttributeAlphaState(0),
435 mnAttributePositionState(0),
436 mnAttributeContentState(0),
437 mnAttributeVisibilityState(0),
439 mxComponentContext( rContext
.mxComponentContext
),
440 maHyperlinkIndices(),
441 maHyperlinkRegions(),
443 mnIsAnimatedCount(0),
444 mnAnimationLoopCount(0),
446 mbForceUpdate( false ),
447 mbAttributeLayerRevoked( false ),
448 mbDrawingLayerAnim( false )
450 ENSURE_OR_THROW( rGraphic
.IsAnimated(),
451 "DrawShape::DrawShape(): Graphic is no animation" );
453 getAnimationFromGraphic( maAnimationFrames
,
454 mnAnimationLoopCount
,
457 ENSURE_OR_THROW( !maAnimationFrames
.empty() &&
458 maAnimationFrames
.front().mpMtf
,
459 "DrawShape::DrawShape(): " );
460 mpCurrMtf
= maAnimationFrames
.front().mpMtf
;
462 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
463 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
464 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
467 DrawShape::DrawShape( const DrawShape
& rSrc
,
468 const DocTreeNode
& rTreeNode
,
470 mxShape( rSrc
.mxShape
),
471 mxPage( rSrc
.mxPage
),
472 maAnimationFrames(), // don't copy animations for subsets,
473 // only the current frame!
475 mpCurrMtf( rSrc
.mpCurrMtf
),
476 mnCurrMtfLoadFlags( rSrc
.mnCurrMtfLoadFlags
),
477 maCurrentShapeUnitBounds(),
479 maBounds( rSrc
.maBounds
),
481 mpIntrinsicAnimationActivity(),
482 mnAttributeTransformationState(0),
483 mnAttributeClipState(0),
484 mnAttributeAlphaState(0),
485 mnAttributePositionState(0),
486 mnAttributeContentState(0),
487 mnAttributeVisibilityState(0),
489 mxComponentContext( rSrc
.mxComponentContext
),
490 maHyperlinkIndices(),
491 maHyperlinkRegions(),
492 maSubsetting( rTreeNode
, mpCurrMtf
),
493 mnIsAnimatedCount(0),
494 mnAnimationLoopCount(0),
495 mbIsVisible( rSrc
.mbIsVisible
),
496 mbForceUpdate( false ),
497 mbAttributeLayerRevoked( false ),
498 mbDrawingLayerAnim( false )
500 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
501 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
503 // xxx todo: currently not implemented for subsetted shapes;
504 // would mean modifying set of hyperlink regions when
505 // subsetting text portions. N.B.: there's already an
506 // issue for this #i72828#
513 DrawShapeSharedPtr
DrawShape::create(
514 const uno::Reference
< drawing::XShape
>& xShape
,
515 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
518 const SlideShowContext
& rContext
)
520 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
526 if( pShape
->hasIntrinsicAnimation() )
528 OSL_ASSERT( pShape
->maAnimationFrames
.empty() );
529 if( pShape
->getNumberOfTreeNodes(
530 DocTreeNode::NodeType::LogicalParagraph
) > 0 )
532 pShape
->mpIntrinsicAnimationActivity
=
533 createDrawingLayerAnimActivity(
539 if( pShape
->hasHyperlinks() )
540 rContext
.mpSubsettableShapeManager
->addHyperlinkArea( pShape
);
545 DrawShapeSharedPtr
DrawShape::create(
546 const uno::Reference
< drawing::XShape
>& xShape
,
547 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
549 const Graphic
& rGraphic
,
550 const SlideShowContext
& rContext
)
552 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
558 if( pShape
->hasIntrinsicAnimation() )
560 OSL_ASSERT( !pShape
->maAnimationFrames
.empty() );
562 std::vector
<double> aTimeout
;
564 pShape
->maAnimationFrames
.begin(),
565 pShape
->maAnimationFrames
.end(),
566 std::back_insert_iterator
< std::vector
<double> >( aTimeout
),
567 std::mem_fn(&MtfAnimationFrame::getDuration
) );
569 WakeupEventSharedPtr
pWakeupEvent(
570 new WakeupEvent( rContext
.mrEventQueue
.getTimer(),
571 rContext
.mrActivitiesQueue
) );
573 ActivitySharedPtr pActivity
=
574 createIntrinsicAnimationActivity(
579 pShape
->mnAnimationLoopCount
);
581 pWakeupEvent
->setActivity( pActivity
);
582 pShape
->mpIntrinsicAnimationActivity
= pActivity
;
585 OSL_ENSURE( !pShape
->hasHyperlinks(),
586 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
591 DrawShape::~DrawShape()
595 // dispose intrinsic animation activity, else, it will
597 ActivitySharedPtr
pActivity( mpIntrinsicAnimationActivity
.lock() );
599 pActivity
->dispose();
601 catch (uno::Exception
const &)
603 DBG_UNHANDLED_EXCEPTION("slideshow");
607 uno::Reference
< drawing::XShape
> DrawShape::getXShape() const
612 void DrawShape::addViewLayer( const ViewLayerSharedPtr
& rNewLayer
,
616 if( ::std::any_of( maViewShapes
.begin(),
619 ( const ViewShapeSharedPtr
& pShape
)
620 { return rNewLayer
== pShape
->getViewLayer(); } ) )
622 // yes, nothing to do
626 ViewShapeSharedPtr
pNewShape( new ViewShape( rNewLayer
) );
628 maViewShapes
.push_back( pNewShape
);
630 // pass on animation state
631 if( mnIsAnimatedCount
)
633 for( int i
=0; i
<mnIsAnimatedCount
; ++i
)
634 pNewShape
->enterAnimationMode();
637 // render the Shape on the newly added ViewLayer
640 pNewShape
->update( mpCurrMtf
,
647 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr
& rLayer
)
649 const ViewShapeVector::iterator
aEnd( maViewShapes
.end() );
651 OSL_ENSURE( ::std::count_if(maViewShapes
.begin(),
654 ( const ViewShapeSharedPtr
& pShape
)
655 { return rLayer
== pShape
->getViewLayer(); } ) < 2,
656 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
658 ViewShapeVector::iterator aIter
;
660 if( (aIter
=::std::remove_if( maViewShapes
.begin(),
663 ( const ViewShapeSharedPtr
& pShape
)
664 { return rLayer
== pShape
->getViewLayer(); } ) ) == aEnd
)
666 // view layer seemingly was not added, failed
670 // actually erase from container
671 maViewShapes
.erase( aIter
, aEnd
);
676 void DrawShape::clearAllViewLayers()
678 maViewShapes
.clear();
681 bool DrawShape::update() const
689 return implRender( getUpdateFlags() );
693 bool DrawShape::render() const
695 // force redraw. Have to also pass on the update flags,
696 // because e.g. content update (regeneration of the
697 // metafile renderer) is normally not performed. A simple
698 // UpdateFlags::Force would only paint the metafile in its
700 return implRender( UpdateFlags::Force
| getUpdateFlags() );
703 bool DrawShape::isContentChanged() const
705 return mbForceUpdate
||
706 getUpdateFlags() != UpdateFlags::NONE
;
710 ::basegfx::B2DRectangle
DrawShape::getBounds() const
712 // little optimization: for non-modified shapes, we don't
713 // create an ShapeAttributeStack, and therefore also don't
715 return getShapePosSize( maBounds
,
719 ::basegfx::B2DRectangle
DrawShape::getDomBounds() const
724 ::basegfx::B2DRectangle
DrawShape::getUpdateArea() const
726 ::basegfx::B2DRectangle aBounds
;
728 // an already empty shape bound need no further
729 // treatment. In fact, any changes applied below would
730 // actually remove the special empty state, thus, don't
732 if( !maBounds
.isEmpty() )
734 basegfx::B2DRectangle
aUnitBounds(0.0,0.0,1.0,1.0);
736 if( !maViewShapes
.empty() )
737 aUnitBounds
= getActualUnitShapeBounds();
739 if( !aUnitBounds
.isEmpty() )
741 if( mpAttributeLayer
)
743 // calc actual shape area (in user coordinate
744 // space) from the transformation as given by the
745 // shape attribute layer
746 aBounds
= getShapeUpdateArea( aUnitBounds
,
747 getShapeTransformation( getBounds(),
753 // no attribute layer, thus, the true shape bounds
754 // can be directly derived from the XShape bound
756 aBounds
= getShapeUpdateArea( aUnitBounds
,
760 if( !maViewShapes
.empty() )
762 // determine border needed for antialiasing the shape
763 ::basegfx::B2DSize
aAABorder(0.0,0.0);
765 // for every view, get AA border and 'expand' aAABorder
767 for( const auto& rViewShape
: maViewShapes
)
769 const ::basegfx::B2DSize
rShapeBorder( rViewShape
->getAntialiasingBorder() );
771 aAABorder
.setX( ::std::max(
773 aAABorder
.getX() ) );
774 aAABorder
.setY( ::std::max(
776 aAABorder
.getY() ) );
779 // add calculated AA border to aBounds
780 aBounds
= ::basegfx::B2DRectangle( aBounds
.getMinX() - aAABorder
.getX(),
781 aBounds
.getMinY() - aAABorder
.getY(),
782 aBounds
.getMaxX() + aAABorder
.getX(),
783 aBounds
.getMaxY() + aAABorder
.getY() );
791 bool DrawShape::isVisible() const
793 bool bIsVisible( mbIsVisible
);
795 if( mpAttributeLayer
)
797 // check whether visibility and alpha are not default
798 // (mpAttributeLayer->isVisibilityValid() returns true
799 // then): bVisible becomes true, if shape visibility
800 // is on and alpha is not 0.0 (fully transparent)
801 if( mpAttributeLayer
->isVisibilityValid() )
802 bIsVisible
= mpAttributeLayer
->getVisibility();
804 // only touch bIsVisible, if the shape is still
805 // visible - if getVisibility already made us
806 // invisible, no alpha value will make us appear
808 if( bIsVisible
&& mpAttributeLayer
->isAlphaValid() )
809 bIsVisible
= !::basegfx::fTools::equalZero( mpAttributeLayer
->getAlpha() );
815 double DrawShape::getPriority() const
820 bool DrawShape::isBackgroundDetached() const
822 return mnIsAnimatedCount
> 0;
825 bool DrawShape::hasIntrinsicAnimation() const
827 return (!maAnimationFrames
.empty() || mbDrawingLayerAnim
);
830 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame
)
832 ENSURE_OR_RETURN_VOID( nCurrFrame
< maAnimationFrames
.size(),
833 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
835 if( mnCurrFrame
!= nCurrFrame
)
837 mnCurrFrame
= nCurrFrame
;
838 mpCurrMtf
= maAnimationFrames
[ mnCurrFrame
].mpMtf
;
839 mbForceUpdate
= true;
844 void DrawShape::prepareHyperlinkIndices() const
846 if ( !maHyperlinkIndices
.empty())
848 maHyperlinkIndices
.clear();
849 maHyperlinkRegions
.clear();
852 sal_Int32 nIndex
= 0;
853 for ( MetaAction
* pCurrAct
= mpCurrMtf
->FirstAction();
854 pCurrAct
!= nullptr; pCurrAct
= mpCurrMtf
->NextAction() )
856 if (pCurrAct
->GetType() == MetaActionType::COMMENT
) {
857 MetaCommentAction
* pAct
=
858 static_cast<MetaCommentAction
*>(pCurrAct
);
859 // skip comment if not a special XTEXT comment
860 if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
861 // e.g. date field doesn't have data!
862 // currently assuming that only url field, this is
863 // somehow fragile! xxx todo if possible
864 pAct
->GetData() != nullptr &&
865 pAct
->GetDataSize() > 0)
867 if (!maHyperlinkIndices
.empty() &&
868 maHyperlinkIndices
.back().second
== -1) {
869 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
870 maHyperlinkIndices
.pop_back();
871 maHyperlinkRegions
.pop_back();
873 maHyperlinkIndices
.emplace_back( nIndex
+ 1,
874 -1 /* to be filled below */ );
875 maHyperlinkRegions
.emplace_back(
876 basegfx::B2DRectangle(),
878 reinterpret_cast<sal_Unicode
const*>(
880 pAct
->GetDataSize() / sizeof(sal_Unicode
) )
883 else if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
884 // pending end is expected:
885 !maHyperlinkIndices
.empty() &&
886 maHyperlinkIndices
.back().second
== -1)
888 maHyperlinkIndices
.back().second
= nIndex
;
893 nIndex
+= getNextActionOffset(pCurrAct
);
895 if (!maHyperlinkIndices
.empty() &&
896 maHyperlinkIndices
.back().second
== -1) {
897 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
898 maHyperlinkIndices
.pop_back();
899 maHyperlinkRegions
.pop_back();
901 OSL_ASSERT( maHyperlinkIndices
.size() == maHyperlinkRegions
.size());
904 bool DrawShape::hasHyperlinks() const
906 return ! maHyperlinkRegions
.empty();
909 HyperlinkArea::HyperlinkRegions
DrawShape::getHyperlinkRegions() const
911 OSL_ASSERT( !maViewShapes
.empty() );
914 return HyperlinkArea::HyperlinkRegions();
916 // late init, determine regions:
917 if( !maHyperlinkRegions
.empty() &&
918 !maViewShapes
.empty() &&
919 // region already inited?
920 maHyperlinkRegions
.front().first
.getWidth() == 0 &&
921 maHyperlinkRegions
.front().first
.getHeight() == 0 &&
922 maHyperlinkRegions
.size() == maHyperlinkIndices
.size() )
924 // TODO(Q2): Although this _is_ currently
925 // view-agnostic, it might not stay like that.
926 ViewShapeSharedPtr
const& pViewShape
= maViewShapes
.front();
927 cppcanvas::CanvasSharedPtr
const pCanvas(
928 pViewShape
->getViewLayer()->getCanvas() );
930 // reuse Renderer of first view shape:
931 cppcanvas::RendererSharedPtr
const pRenderer(
932 pViewShape
->getRenderer(
933 pCanvas
, mpCurrMtf
, mpAttributeLayer
) );
935 OSL_ASSERT( pRenderer
);
939 basegfx::B2DHomMatrix
const aOldTransform(
940 pCanvas
->getTransformation() );
941 basegfx::B2DHomMatrix aTransform
;
942 pCanvas
->setTransformation( aTransform
/* empty */ );
945 ::cppcanvas::Canvas
* pTmpCanvas
= pCanvas
.get();
946 comphelper::ScopeGuard
const resetOldTransformation(
947 [&aOldTransform
, &pTmpCanvas
]()
948 { return pTmpCanvas
->setTransformation( aOldTransform
); } );
950 aTransform
.scale( maBounds
.getWidth(),
951 maBounds
.getHeight() );
952 pRenderer
->setTransformation( aTransform
);
953 pRenderer
->setClip();
955 for( std::size_t pos
= maHyperlinkRegions
.size(); pos
--; )
958 HyperlinkIndexPair
const& rIndices
= maHyperlinkIndices
[pos
];
959 basegfx::B2DRectangle
const region(
960 pRenderer
->getSubsetArea( rIndices
.first
,
962 maHyperlinkRegions
[pos
].first
= region
;
967 // shift shape-relative hyperlink regions to
968 // slide-absolute position
970 HyperlinkRegions aTranslatedRegions
;
972 // increase capacity to same size as the container for
973 // shape-relative hyperlink regions to avoid reallocation
974 aTranslatedRegions
.reserve( maHyperlinkRegions
.size() );
975 const basegfx::B2DPoint
& rOffset(getBounds().getMinimum());
976 for( const auto& cp
: maHyperlinkRegions
)
978 basegfx::B2DRange
const& relRegion( cp
.first
);
979 aTranslatedRegions
.emplace_back(
981 relRegion
.getMinimum() + rOffset
,
982 relRegion
.getMaximum() + rOffset
),
986 return aTranslatedRegions
;
989 double DrawShape::getHyperlinkPriority() const
991 return getPriority();
995 // AnimatableShape methods
998 void DrawShape::enterAnimationMode()
1000 OSL_ENSURE( !maViewShapes
.empty(),
1001 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1003 if( mnIsAnimatedCount
== 0 )
1005 // notify all ViewShapes, by calling their enterAnimationMode method.
1006 // We're now entering animation mode
1007 for( const auto& rViewShape
: maViewShapes
)
1008 rViewShape
->enterAnimationMode();
1011 ++mnIsAnimatedCount
;
1014 void DrawShape::leaveAnimationMode()
1016 OSL_ENSURE( !maViewShapes
.empty(),
1017 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1019 --mnIsAnimatedCount
;
1021 if( mnIsAnimatedCount
== 0 )
1023 // notify all ViewShapes, by calling their leaveAnimationMode method.
1024 // we're now leaving animation mode
1025 for( const auto& rViewShape
: maViewShapes
)
1026 rViewShape
->leaveAnimationMode();
1031 // AttributableShape methods
1034 ShapeAttributeLayerSharedPtr
DrawShape::createAttributeLayer()
1036 // create new layer, with last as its new child
1037 mpAttributeLayer
.reset( new ShapeAttributeLayer( mpAttributeLayer
) );
1039 // Update the local state ids to reflect those of the new layer.
1042 return mpAttributeLayer
;
1045 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr
& rLayer
)
1047 if( !mpAttributeLayer
)
1048 return false; // no layers
1050 if( mpAttributeLayer
== rLayer
)
1052 // it's the toplevel layer
1053 mpAttributeLayer
= mpAttributeLayer
->getChildLayer();
1055 // force content redraw, all state variables have
1057 mbAttributeLayerRevoked
= true;
1063 // pass on to the layer, to try its children
1064 return mpAttributeLayer
->revokeChildLayer( rLayer
);
1068 ShapeAttributeLayerSharedPtr
DrawShape::getTopmostAttributeLayer() const
1070 return mpAttributeLayer
;
1073 void DrawShape::setVisibility( bool bVisible
)
1075 if( mbIsVisible
!= bVisible
)
1077 mbIsVisible
= bVisible
;
1078 mbForceUpdate
= true;
1082 const DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier() const
1087 DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier()
1092 DocTreeNode
DrawShape::getSubsetNode() const
1094 // forward to delegate
1095 return maSubsetting
.getSubsetNode();
1098 AttributableShapeSharedPtr
DrawShape::getSubset( const DocTreeNode
& rTreeNode
) const
1100 // forward to delegate
1101 return maSubsetting
.getSubsetShape( rTreeNode
);
1104 bool DrawShape::createSubset( AttributableShapeSharedPtr
& o_rSubset
,
1105 const DocTreeNode
& rTreeNode
)
1107 // subset shape already created for this DocTreeNode?
1108 AttributableShapeSharedPtr
pSubset( maSubsetting
.getSubsetShape( rTreeNode
) );
1110 // when true, this method has created a new subset
1112 bool bNewlyCreated( false );
1116 o_rSubset
= pSubset
;
1118 // reusing existing subset
1122 // not yet created, init entry
1123 o_rSubset
.reset( new DrawShape( *this,
1125 // TODO(Q3): That's a
1127 // that start and end
1128 // index will always
1129 // be less than 65535
1131 rTreeNode
.getStartIndex()/double(SAL_MAX_INT16
) ));
1133 bNewlyCreated
= true; // subset newly created
1136 // always register shape at DrawShapeSubsetting, to keep
1137 // refcount up-to-date
1138 maSubsetting
.addSubsetShape( o_rSubset
);
1140 // flush bounds cache
1141 maCurrentShapeUnitBounds
.reset();
1143 return bNewlyCreated
;
1146 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr
& rShape
)
1148 // flush bounds cache
1149 maCurrentShapeUnitBounds
.reset();
1151 // forward to delegate
1152 if( maSubsetting
.revokeSubsetShape( rShape
) )
1154 // force redraw, our content has possibly changed (as
1155 // one of the subsets now display within our shape
1157 mbForceUpdate
= true;
1159 // #i47428# TEMP FIX: synchronize visibility of subset
1162 // TODO(F3): Remove here, and implement
1163 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1164 // additional level of indirection: create a
1165 // persistent subset, containing all text/only the
1166 // background respectively. From _that_ object,
1167 // generate the temporary character subset shapes.
1168 const ShapeAttributeLayerSharedPtr
& rAttrLayer(
1169 rShape
->getTopmostAttributeLayer() );
1171 rAttrLayer
->isVisibilityValid() &&
1172 rAttrLayer
->getVisibility() != isVisible() )
1174 const bool bVisibility( rAttrLayer
->getVisibility() );
1176 // visibilities differ - adjust ours, then
1177 if( mpAttributeLayer
)
1178 mpAttributeLayer
->setVisibility( bVisibility
);
1180 mbIsVisible
= bVisibility
;
1191 sal_Int32
DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1193 return maSubsetting
.getNumberOfTreeNodes( eNodeType
);
1196 DocTreeNode
DrawShape::getTreeNode( sal_Int32 nNodeIndex
,
1197 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1199 if ( hasHyperlinks())
1201 prepareHyperlinkIndices();
1204 return maSubsetting
.getTreeNode( nNodeIndex
, eNodeType
);
1207 sal_Int32
DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode
& rParentNode
,
1208 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1210 return maSubsetting
.getNumberOfSubsetTreeNodes( rParentNode
, eNodeType
);
1213 DocTreeNode
DrawShape::getSubsetTreeNode( const DocTreeNode
& rParentNode
,
1214 sal_Int32 nNodeIndex
,
1215 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1217 return maSubsetting
.getSubsetTreeNode( rParentNode
, nNodeIndex
, eNodeType
);
1222 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */