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 <com/sun/star/awt/Rectangle.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/awt/FontWeight.hpp>
26 #include <comphelper/anytostring.hxx>
27 #include <cppuhelper/exc_hlp.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/gdimtf.hxx>
31 #include <vcl/wrkwin.hxx>
33 #include <basegfx/numeric/ftools.hxx>
35 #include <rtl/math.hxx>
37 #include <com/sun/star/drawing/TextAnimationKind.hpp>
39 #include <vcl/svapp.hxx>
40 #include <vcl/window.hxx>
41 #include <tools/stream.hxx>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/datatransfer/XTransferable.hpp>
45 #include <comphelper/scopeguard.hxx>
46 #include <canvas/canvastools.hxx>
54 #include "drawshapesubsetting.hxx"
55 #include "drawshape.hxx"
56 #include <eventqueue.hxx>
57 #include <wakeupevent.hxx>
58 #include <subsettableshapemanager.hxx>
59 #include "intrinsicanimationactivity.hxx"
60 #include <slideshowexceptions.hxx>
62 #include "gdimtftools.hxx"
63 #include "drawinglayeranimation.hxx"
67 using namespace ::com::sun::star
;
79 GDIMetaFileSharedPtr
const & DrawShape::forceScrollTextMetaFile()
81 if ((mnCurrMtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != MTF_LOAD_SCROLL_TEXT_MTF
)
83 // reload with added flags:
84 mnCurrMtfLoadFlags
|= MTF_LOAD_SCROLL_TEXT_MTF
;
85 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(mxShape
, uno::UNO_QUERY
),
86 mxPage
, mnCurrMtfLoadFlags
,
90 mpCurrMtf
.reset( new GDIMetaFile
);
92 // TODO(F1): Currently, the scroll metafile will
93 // never contain any verbose text comments. Thus,
94 // can only display the full mtf content, no
96 maSubsetting
.reset( mpCurrMtf
);
98 // adapt maBounds. the requested scroll text metafile
99 // will typically have dimension different from the
101 ::basegfx::B2DRectangle aScrollRect
, aPaintRect
;
102 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect
,
105 "DrawShape::forceScrollTextMetaFile(): Could "
106 "not extract scroll anim rectangles from mtf" );
108 // take the larger one of the two rectangles (that
109 // should be the bound rect of the retrieved
111 if( aScrollRect
.isInside( aPaintRect
) )
112 maBounds
= aScrollRect
;
114 maBounds
= aPaintRect
;
119 void DrawShape::updateStateIds() const
121 // Update the states, we've just redrawn or created a new
123 if( mpAttributeLayer
)
125 mnAttributeTransformationState
= mpAttributeLayer
->getTransformationState();
126 mnAttributeClipState
= mpAttributeLayer
->getClipState();
127 mnAttributeAlphaState
= mpAttributeLayer
->getAlphaState();
128 mnAttributePositionState
= mpAttributeLayer
->getPositionState();
129 mnAttributeContentState
= mpAttributeLayer
->getContentState();
130 mnAttributeVisibilityState
= mpAttributeLayer
->getVisibilityState();
134 ViewShape::RenderArgs
DrawShape::getViewRenderArgs() const
136 return ViewShape::RenderArgs(
140 getActualUnitShapeBounds(),
142 maSubsetting
.getActiveSubsets(),
146 bool DrawShape::implRender( UpdateFlags nUpdateFlags
) const
148 SAL_INFO( "slideshow", "::presentation::internal::DrawShape::implRender()" );
149 SAL_INFO( "slideshow", "::presentation::internal::DrawShape: 0x" << std::hex
<< this );
151 // will perform the update now, clear update-enforcing
153 mbForceUpdate
= false;
154 mbAttributeLayerRevoked
= false;
156 ENSURE_OR_RETURN_FALSE( !maViewShapes
.empty(),
157 "DrawShape::implRender(): render called on DrawShape without views" );
159 if( maBounds
.isEmpty() )
161 // zero-sized shapes are effectively invisible,
162 // thus, we save us the rendering...
166 // redraw all view shapes, by calling their update() method
167 ViewShape::RenderArgs
renderArgs( getViewRenderArgs() );
168 bool bVisible
= isVisible();
169 if( ::std::count_if( maViewShapes
.begin(),
171 [this, &bVisible
, &renderArgs
, &nUpdateFlags
]
172 ( const ViewShapeSharedPtr
& pShape
)
173 { return pShape
->update( this->mpCurrMtf
,
177 != static_cast<ViewShapeVector::difference_type
>(maViewShapes
.size()) )
179 // at least one of the ViewShape::update() calls did return
180 // false - update failed on at least one ViewLayer
184 // successfully redrawn - update state IDs to detect next changes
190 UpdateFlags
DrawShape::getUpdateFlags() const
192 // default: update nothing, unless ShapeAttributeStack
193 // tells us below, or if the attribute layer was revoked
194 UpdateFlags
nUpdateFlags(UpdateFlags::NONE
);
196 // possibly the whole shape content changed
197 if( mbAttributeLayerRevoked
)
198 nUpdateFlags
= UpdateFlags::Content
;
201 // determine what has to be updated
204 // do we have an attribute layer?
205 if( mpAttributeLayer
)
207 // Prevent nUpdateFlags to be modified when the shape is not
208 // visible, except when it just was hidden.
209 if (mpAttributeLayer
->getVisibility()
210 || mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
212 if (mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
214 // Change of the visibility state is mapped to
215 // content change because when the visibility
216 // changes then usually a sprite is shown or hidden
217 // and the background under has to be painted once.
218 nUpdateFlags
|= UpdateFlags::Content
;
221 // TODO(P1): This can be done without conditional branching.
223 if( mpAttributeLayer
->getPositionState() != mnAttributePositionState
)
225 nUpdateFlags
|= UpdateFlags::Position
;
227 if( mpAttributeLayer
->getAlphaState() != mnAttributeAlphaState
)
229 nUpdateFlags
|= UpdateFlags::Alpha
;
231 if( mpAttributeLayer
->getClipState() != mnAttributeClipState
)
233 nUpdateFlags
|= UpdateFlags::Clip
;
235 if( mpAttributeLayer
->getTransformationState() != mnAttributeTransformationState
)
237 nUpdateFlags
|= UpdateFlags::Transformation
;
239 if( mpAttributeLayer
->getContentState() != mnAttributeContentState
)
241 nUpdateFlags
|= UpdateFlags::Content
;
249 ::basegfx::B2DRectangle
DrawShape::getActualUnitShapeBounds() const
251 ENSURE_OR_THROW( !maViewShapes
.empty(),
252 "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
254 const VectorOfDocTreeNodes
& rSubsets(
255 maSubsetting
.getActiveSubsets() );
257 const ::basegfx::B2DRectangle
aDefaultBounds( 0.0,0.0,1.0,1.0 );
259 // perform the cheapest check first
260 if( rSubsets
.empty() )
262 // if subset contains the whole shape, no need to call
263 // the somewhat expensive bound calculation, since as
264 // long as the subset is empty, this branch will be
266 return aDefaultBounds
;
270 OSL_ENSURE( rSubsets
.size() != 1 ||
271 !rSubsets
.front().isEmpty(),
272 "DrawShape::getActualUnitShapeBounds() expects a "
273 "_non-empty_ subset vector for a subsetted shape!" );
275 // are the cached bounds still valid?
276 if( !maCurrentShapeUnitBounds
)
278 // no, (re)generate them
279 // =====================
281 // setup cached values to defaults (might fail to
282 // retrieve true bounds below)
283 maCurrentShapeUnitBounds
.reset( aDefaultBounds
);
285 // TODO(P2): the subset of the master shape (that from
286 // which the subsets are subtracted) changes
287 // relatively often (every time a subset shape is
288 // added or removed). Maybe we should exclude it here,
289 // always assuming full bounds?
291 ::cppcanvas::CanvasSharedPtr
pDestinationCanvas(
292 maViewShapes
.front()->getViewLayer()->getCanvas() );
294 // TODO(Q2): Although this _is_ currently
295 // view-agnostic, it might not stay like
296 // that. Maybe this method should again be moved
298 ::cppcanvas::RendererSharedPtr
pRenderer(
299 maViewShapes
.front()->getRenderer(
300 pDestinationCanvas
, mpCurrMtf
, mpAttributeLayer
) );
302 // If we cannot not prefetch, be defensive and assume
306 // temporarily, switch total transformation to identity
307 // (need the bounds in the [0,1]x[0,1] unit coordinate
309 ::basegfx::B2DHomMatrix aEmptyTransformation
;
311 ::basegfx::B2DHomMatrix
aOldTransform( pDestinationCanvas
->getTransformation() );
312 pDestinationCanvas
->setTransformation( aEmptyTransformation
);
313 pRenderer
->setTransformation( aEmptyTransformation
);
315 // restore old transformation when leaving the scope
316 const ::comphelper::ScopeGuard
aGuard(
317 [&pDestinationCanvas
, &aOldTransform
]()
318 { return pDestinationCanvas
->setTransformation( aOldTransform
); } );
321 // retrieve bounds for subset of whole metafile
324 ::basegfx::B2DRange aTotalBounds
;
326 // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
328 for( const auto& rDocTreeNode
: rSubsets
)
329 aTotalBounds
.expand( pRenderer
->getSubsetArea(
330 rDocTreeNode
.getStartIndex(),
331 rDocTreeNode
.getEndIndex() ) );
333 OSL_ENSURE( aTotalBounds
.getMinX() >= -0.1 &&
334 aTotalBounds
.getMinY() >= -0.1 &&
335 aTotalBounds
.getMaxX() <= 1.1 &&
336 aTotalBounds
.getMaxY() <= 1.1,
337 "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
339 // really make sure no shape appears larger than its
340 // original bounds (there _are_ some pathologic cases,
341 // especially when imported from PPT, that have
342 // e.g. obscenely large polygon bounds)
343 aTotalBounds
.intersect(
344 ::basegfx::B2DRange( 0.0, 0.0,
347 maCurrentShapeUnitBounds
.reset( aTotalBounds
);
351 return *maCurrentShapeUnitBounds
;
355 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
356 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
359 const SlideShowContext
& rContext
) :
361 mxPage( xContainingPage
),
362 maAnimationFrames(), // empty, we don't have no intrinsic animation
365 mnCurrMtfLoadFlags( bForeignSource
366 ? MTF_LOAD_FOREIGN_SOURCE
: MTF_LOAD_NONE
),
367 maCurrentShapeUnitBounds(),
368 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
369 maBounds( getAPIShapeBounds( xShape
) ),
371 mpIntrinsicAnimationActivity(),
372 mnAttributeTransformationState(0),
373 mnAttributeClipState(0),
374 mnAttributeAlphaState(0),
375 mnAttributePositionState(0),
376 mnAttributeContentState(0),
377 mnAttributeVisibilityState(0),
379 mxComponentContext( rContext
.mxComponentContext
),
380 maHyperlinkIndices(),
381 maHyperlinkRegions(),
383 mnIsAnimatedCount(0),
384 mnAnimationLoopCount(0),
386 mbForceUpdate( false ),
387 mbAttributeLayerRevoked( false ),
388 mbDrawingLayerAnim( false )
390 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
391 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
393 // check for drawing layer animations:
394 drawing::TextAnimationKind eKind
= drawing::TextAnimationKind_NONE
;
395 uno::Reference
<beans::XPropertySet
> xPropSet( mxShape
,
398 getPropertyValue( eKind
, xPropSet
,
399 "TextAnimationKind" );
400 mbDrawingLayerAnim
= (eKind
!= drawing::TextAnimationKind_NONE
);
402 // must NOT be called from within initializer list, uses
403 // state from mnCurrMtfLoadFlags!
404 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY
),
405 xContainingPage
, mnCurrMtfLoadFlags
,
406 mxComponentContext
);
408 mpCurrMtf
.reset(new GDIMetaFile
);
410 maSubsetting
.reset( mpCurrMtf
);
412 prepareHyperlinkIndices();
415 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
416 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
418 const Graphic
& rGraphic
,
419 const SlideShowContext
& rContext
) :
421 mxPage( xContainingPage
),
425 mnCurrMtfLoadFlags( MTF_LOAD_NONE
),
426 maCurrentShapeUnitBounds(),
427 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
428 maBounds( getAPIShapeBounds( xShape
) ),
430 mpIntrinsicAnimationActivity(),
431 mnAttributeTransformationState(0),
432 mnAttributeClipState(0),
433 mnAttributeAlphaState(0),
434 mnAttributePositionState(0),
435 mnAttributeContentState(0),
436 mnAttributeVisibilityState(0),
438 mxComponentContext( rContext
.mxComponentContext
),
439 maHyperlinkIndices(),
440 maHyperlinkRegions(),
442 mnIsAnimatedCount(0),
443 mnAnimationLoopCount(0),
445 mbForceUpdate( false ),
446 mbAttributeLayerRevoked( false ),
447 mbDrawingLayerAnim( false )
449 ENSURE_OR_THROW( rGraphic
.IsAnimated(),
450 "DrawShape::DrawShape(): Graphic is no animation" );
452 getAnimationFromGraphic( maAnimationFrames
,
453 mnAnimationLoopCount
,
456 ENSURE_OR_THROW( !maAnimationFrames
.empty() &&
457 maAnimationFrames
.front().mpMtf
,
458 "DrawShape::DrawShape(): " );
459 mpCurrMtf
= maAnimationFrames
.front().mpMtf
;
461 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
462 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
463 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
466 DrawShape::DrawShape( const DrawShape
& rSrc
,
467 const DocTreeNode
& rTreeNode
,
469 mxShape( rSrc
.mxShape
),
470 mxPage( rSrc
.mxPage
),
471 maAnimationFrames(), // don't copy animations for subsets,
472 // only the current frame!
474 mpCurrMtf( rSrc
.mpCurrMtf
),
475 mnCurrMtfLoadFlags( rSrc
.mnCurrMtfLoadFlags
),
476 maCurrentShapeUnitBounds(),
478 maBounds( rSrc
.maBounds
),
480 mpIntrinsicAnimationActivity(),
481 mnAttributeTransformationState(0),
482 mnAttributeClipState(0),
483 mnAttributeAlphaState(0),
484 mnAttributePositionState(0),
485 mnAttributeContentState(0),
486 mnAttributeVisibilityState(0),
488 mxComponentContext( rSrc
.mxComponentContext
),
489 maHyperlinkIndices(),
490 maHyperlinkRegions(),
491 maSubsetting( rTreeNode
, mpCurrMtf
),
492 mnIsAnimatedCount(0),
493 mnAnimationLoopCount(0),
494 mbIsVisible( rSrc
.mbIsVisible
),
495 mbForceUpdate( false ),
496 mbAttributeLayerRevoked( false ),
497 mbDrawingLayerAnim( false )
499 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
500 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
502 // xxx todo: currently not implemented for subsetted shapes;
503 // would mean modifying set of hyperlink regions when
504 // subsetting text portions. N.B.: there's already an
505 // issue for this #i72828#
512 DrawShapeSharedPtr
DrawShape::create(
513 const uno::Reference
< drawing::XShape
>& xShape
,
514 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
517 const SlideShowContext
& rContext
)
519 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
525 if( pShape
->hasIntrinsicAnimation() )
527 OSL_ASSERT( pShape
->maAnimationFrames
.empty() );
528 if( pShape
->getNumberOfTreeNodes(
529 DocTreeNode::NodeType::LogicalParagraph
) > 0 )
531 pShape
->mpIntrinsicAnimationActivity
=
532 createDrawingLayerAnimActivity(
538 if( pShape
->hasHyperlinks() )
539 rContext
.mpSubsettableShapeManager
->addHyperlinkArea( pShape
);
544 DrawShapeSharedPtr
DrawShape::create(
545 const uno::Reference
< drawing::XShape
>& xShape
,
546 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
548 const Graphic
& rGraphic
,
549 const SlideShowContext
& rContext
)
551 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
557 if( pShape
->hasIntrinsicAnimation() )
559 OSL_ASSERT( !pShape
->maAnimationFrames
.empty() );
561 std::vector
<double> aTimeout
;
563 pShape
->maAnimationFrames
.begin(),
564 pShape
->maAnimationFrames
.end(),
565 std::back_insert_iterator
< std::vector
<double> >( aTimeout
),
566 std::mem_fn(&MtfAnimationFrame::getDuration
) );
568 WakeupEventSharedPtr
pWakeupEvent(
569 new WakeupEvent( rContext
.mrEventQueue
.getTimer(),
570 rContext
.mrActivitiesQueue
) );
572 ActivitySharedPtr pActivity
=
573 createIntrinsicAnimationActivity(
578 pShape
->mnAnimationLoopCount
);
580 pWakeupEvent
->setActivity( pActivity
);
581 pShape
->mpIntrinsicAnimationActivity
= pActivity
;
584 OSL_ENSURE( !pShape
->hasHyperlinks(),
585 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
590 DrawShape::~DrawShape()
594 // dispose intrinsic animation activity, else, it will
596 ActivitySharedPtr
pActivity( mpIntrinsicAnimationActivity
.lock() );
598 pActivity
->dispose();
600 catch (uno::Exception
&)
602 SAL_WARN( "slideshow", "" << comphelper::anyToString(cppu::getCaughtException() ) );
606 uno::Reference
< drawing::XShape
> DrawShape::getXShape() const
611 void DrawShape::addViewLayer( const ViewLayerSharedPtr
& rNewLayer
,
615 if( ::std::any_of( maViewShapes
.begin(),
618 ( const ViewShapeSharedPtr
& pShape
)
619 { return rNewLayer
== pShape
->getViewLayer(); } ) )
621 // yes, nothing to do
625 ViewShapeSharedPtr
pNewShape( new ViewShape( rNewLayer
) );
627 maViewShapes
.push_back( pNewShape
);
629 // pass on animation state
630 if( mnIsAnimatedCount
)
632 for( int i
=0; i
<mnIsAnimatedCount
; ++i
)
633 pNewShape
->enterAnimationMode();
636 // render the Shape on the newly added ViewLayer
639 pNewShape
->update( mpCurrMtf
,
646 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr
& rLayer
)
648 const ViewShapeVector::iterator
aEnd( maViewShapes
.end() );
650 OSL_ENSURE( ::std::count_if(maViewShapes
.begin(),
653 ( const ViewShapeSharedPtr
& pShape
)
654 { return rLayer
== pShape
->getViewLayer(); } ) < 2,
655 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
657 ViewShapeVector::iterator aIter
;
659 if( (aIter
=::std::remove_if( maViewShapes
.begin(),
662 ( const ViewShapeSharedPtr
& pShape
)
663 { return rLayer
== pShape
->getViewLayer(); } ) ) == aEnd
)
665 // view layer seemingly was not added, failed
669 // actually erase from container
670 maViewShapes
.erase( aIter
, aEnd
);
675 void DrawShape::clearAllViewLayers()
677 maViewShapes
.clear();
680 bool DrawShape::update() const
688 return implRender( getUpdateFlags() );
692 bool DrawShape::render() const
694 // force redraw. Have to also pass on the update flags,
695 // because e.g. content update (regeneration of the
696 // metafile renderer) is normally not performed. A simple
697 // UpdateFlags::Force would only paint the metafile in its
699 return implRender( UpdateFlags::Force
| getUpdateFlags() );
702 bool DrawShape::isContentChanged() const
704 return mbForceUpdate
||
705 getUpdateFlags() != UpdateFlags::NONE
;
709 ::basegfx::B2DRectangle
DrawShape::getBounds() const
711 // little optimization: for non-modified shapes, we don't
712 // create an ShapeAttributeStack, and therefore also don't
714 return getShapePosSize( maBounds
,
718 ::basegfx::B2DRectangle
DrawShape::getDomBounds() const
723 ::basegfx::B2DRectangle
DrawShape::getUpdateArea() const
725 ::basegfx::B2DRectangle aBounds
;
727 // an already empty shape bound need no further
728 // treatment. In fact, any changes applied below would
729 // actually remove the special empty state, thus, don't
731 if( !maBounds
.isEmpty() )
733 basegfx::B2DRectangle
aUnitBounds(0.0,0.0,1.0,1.0);
735 if( !maViewShapes
.empty() )
736 aUnitBounds
= getActualUnitShapeBounds();
738 if( !aUnitBounds
.isEmpty() )
740 if( mpAttributeLayer
)
742 // calc actual shape area (in user coordinate
743 // space) from the transformation as given by the
744 // shape attribute layer
745 aBounds
= getShapeUpdateArea( aUnitBounds
,
746 getShapeTransformation( getBounds(),
752 // no attribute layer, thus, the true shape bounds
753 // can be directly derived from the XShape bound
755 aBounds
= getShapeUpdateArea( aUnitBounds
,
759 if( !maViewShapes
.empty() )
761 // determine border needed for antialiasing the shape
762 ::basegfx::B2DSize
aAABorder(0.0,0.0);
764 // for every view, get AA border and 'expand' aAABorder
766 for( const auto& rViewShape
: maViewShapes
)
768 const ::basegfx::B2DSize
rShapeBorder( rViewShape
->getAntialiasingBorder() );
770 aAABorder
.setX( ::std::max(
772 aAABorder
.getX() ) );
773 aAABorder
.setY( ::std::max(
775 aAABorder
.getY() ) );
778 // add calculated AA border to aBounds
779 aBounds
= ::basegfx::B2DRectangle( aBounds
.getMinX() - aAABorder
.getX(),
780 aBounds
.getMinY() - aAABorder
.getY(),
781 aBounds
.getMaxX() + aAABorder
.getX(),
782 aBounds
.getMaxY() + aAABorder
.getY() );
790 bool DrawShape::isVisible() const
792 bool bIsVisible( mbIsVisible
);
794 if( mpAttributeLayer
)
796 // check whether visibility and alpha are not default
797 // (mpAttributeLayer->isVisibilityValid() returns true
798 // then): bVisible becomes true, if shape visibility
799 // is on and alpha is not 0.0 (fully transparent)
800 if( mpAttributeLayer
->isVisibilityValid() )
801 bIsVisible
= mpAttributeLayer
->getVisibility();
803 // only touch bIsVisible, if the shape is still
804 // visible - if getVisibility already made us
805 // invisible, no alpha value will make us appear
807 if( bIsVisible
&& mpAttributeLayer
->isAlphaValid() )
808 bIsVisible
= !::basegfx::fTools::equalZero( mpAttributeLayer
->getAlpha() );
814 double DrawShape::getPriority() const
819 bool DrawShape::isBackgroundDetached() const
821 return mnIsAnimatedCount
> 0;
824 bool DrawShape::hasIntrinsicAnimation() const
826 return (!maAnimationFrames
.empty() || mbDrawingLayerAnim
);
829 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame
)
831 ENSURE_OR_RETURN_VOID( nCurrFrame
< maAnimationFrames
.size(),
832 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
834 if( mnCurrFrame
!= nCurrFrame
)
836 mnCurrFrame
= nCurrFrame
;
837 mpCurrMtf
= maAnimationFrames
[ mnCurrFrame
].mpMtf
;
838 mbForceUpdate
= true;
843 void DrawShape::prepareHyperlinkIndices() const
845 if ( !maHyperlinkIndices
.empty())
847 maHyperlinkIndices
.clear();
848 maHyperlinkRegions
.clear();
851 sal_Int32 nIndex
= 0;
852 for ( MetaAction
* pCurrAct
= mpCurrMtf
->FirstAction();
853 pCurrAct
!= nullptr; pCurrAct
= mpCurrMtf
->NextAction() )
855 if (pCurrAct
->GetType() == MetaActionType::COMMENT
) {
856 MetaCommentAction
* pAct
=
857 static_cast<MetaCommentAction
*>(pCurrAct
);
858 // skip comment if not a special XTEXT comment
859 if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
860 // e.g. date field doesn't have data!
861 // currently assuming that only url field, this is
862 // somehow fragile! xxx todo if possible
863 pAct
->GetData() != nullptr &&
864 pAct
->GetDataSize() > 0)
866 if (!maHyperlinkIndices
.empty() &&
867 maHyperlinkIndices
.back().second
== -1) {
868 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
869 maHyperlinkIndices
.pop_back();
870 maHyperlinkRegions
.pop_back();
872 maHyperlinkIndices
.emplace_back( nIndex
+ 1,
873 -1 /* to be filled below */ );
874 maHyperlinkRegions
.emplace_back(
875 basegfx::B2DRectangle(),
877 reinterpret_cast<sal_Unicode
const*>(
879 pAct
->GetDataSize() / sizeof(sal_Unicode
) )
882 else if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
883 // pending end is expected:
884 !maHyperlinkIndices
.empty() &&
885 maHyperlinkIndices
.back().second
== -1)
887 maHyperlinkIndices
.back().second
= nIndex
;
892 nIndex
+= getNextActionOffset(pCurrAct
);
894 if (!maHyperlinkIndices
.empty() &&
895 maHyperlinkIndices
.back().second
== -1) {
896 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
897 maHyperlinkIndices
.pop_back();
898 maHyperlinkRegions
.pop_back();
900 OSL_ASSERT( maHyperlinkIndices
.size() == maHyperlinkRegions
.size());
903 bool DrawShape::hasHyperlinks() const
905 return ! maHyperlinkRegions
.empty();
908 HyperlinkArea::HyperlinkRegions
DrawShape::getHyperlinkRegions() const
910 OSL_ASSERT( !maViewShapes
.empty() );
913 return HyperlinkArea::HyperlinkRegions();
915 // late init, determine regions:
916 if( !maHyperlinkRegions
.empty() &&
917 !maViewShapes
.empty() &&
918 // region already inited?
919 maHyperlinkRegions
.front().first
.getWidth() == 0 &&
920 maHyperlinkRegions
.front().first
.getHeight() == 0 &&
921 maHyperlinkRegions
.size() == maHyperlinkIndices
.size() )
923 // TODO(Q2): Although this _is_ currently
924 // view-agnostic, it might not stay like that.
925 ViewShapeSharedPtr
const& pViewShape
= maViewShapes
.front();
926 cppcanvas::CanvasSharedPtr
const pCanvas(
927 pViewShape
->getViewLayer()->getCanvas() );
929 // reuse Renderer of first view shape:
930 cppcanvas::RendererSharedPtr
const pRenderer(
931 pViewShape
->getRenderer(
932 pCanvas
, mpCurrMtf
, mpAttributeLayer
) );
934 OSL_ASSERT( pRenderer
);
938 basegfx::B2DHomMatrix
const aOldTransform(
939 pCanvas
->getTransformation() );
940 basegfx::B2DHomMatrix aTransform
;
941 pCanvas
->setTransformation( aTransform
/* empty */ );
944 ::cppcanvas::Canvas
* pTmpCanvas
= pCanvas
.get();
945 comphelper::ScopeGuard
const resetOldTransformation(
946 [&aOldTransform
, &pTmpCanvas
]()
947 { return pTmpCanvas
->setTransformation( aOldTransform
); } );
949 aTransform
.scale( maBounds
.getWidth(),
950 maBounds
.getHeight() );
951 pRenderer
->setTransformation( aTransform
);
952 pRenderer
->setClip();
954 for( std::size_t pos
= maHyperlinkRegions
.size(); pos
--; )
957 HyperlinkIndexPair
const& rIndices
= maHyperlinkIndices
[pos
];
958 basegfx::B2DRectangle
const region(
959 pRenderer
->getSubsetArea( rIndices
.first
,
961 maHyperlinkRegions
[pos
].first
= region
;
966 // shift shape-relative hyperlink regions to
967 // slide-absolute position
969 HyperlinkRegions aTranslatedRegions
;
971 // increase capacity to same size as the container for
972 // shape-relative hyperlink regions to avoid reallocation
973 aTranslatedRegions
.reserve( maHyperlinkRegions
.size() );
974 const basegfx::B2DPoint
& rOffset(getBounds().getMinimum());
975 for( const auto& cp
: maHyperlinkRegions
)
977 basegfx::B2DRange
const& relRegion( cp
.first
);
978 aTranslatedRegions
.emplace_back(
980 relRegion
.getMinimum() + rOffset
,
981 relRegion
.getMaximum() + rOffset
),
985 return aTranslatedRegions
;
988 double DrawShape::getHyperlinkPriority() const
990 return getPriority();
994 // AnimatableShape methods
997 void DrawShape::enterAnimationMode()
999 OSL_ENSURE( !maViewShapes
.empty(),
1000 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1002 if( mnIsAnimatedCount
== 0 )
1004 // notify all ViewShapes, by calling their enterAnimationMode method.
1005 // We're now entering animation mode
1006 for( const auto& rViewShape
: maViewShapes
)
1007 rViewShape
->enterAnimationMode();
1010 ++mnIsAnimatedCount
;
1013 void DrawShape::leaveAnimationMode()
1015 OSL_ENSURE( !maViewShapes
.empty(),
1016 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1018 --mnIsAnimatedCount
;
1020 if( mnIsAnimatedCount
== 0 )
1022 // notify all ViewShapes, by calling their leaveAnimationMode method.
1023 // we're now leaving animation mode
1024 for( const auto& rViewShape
: maViewShapes
)
1025 rViewShape
->leaveAnimationMode();
1030 // AttributableShape methods
1033 ShapeAttributeLayerSharedPtr
DrawShape::createAttributeLayer()
1035 // create new layer, with last as its new child
1036 mpAttributeLayer
.reset( new ShapeAttributeLayer( mpAttributeLayer
) );
1038 // Update the local state ids to reflect those of the new layer.
1041 return mpAttributeLayer
;
1044 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr
& rLayer
)
1046 if( !mpAttributeLayer
)
1047 return false; // no layers
1049 if( mpAttributeLayer
== rLayer
)
1051 // it's the toplevel layer
1052 mpAttributeLayer
= mpAttributeLayer
->getChildLayer();
1054 // force content redraw, all state variables have
1056 mbAttributeLayerRevoked
= true;
1062 // pass on to the layer, to try its children
1063 return mpAttributeLayer
->revokeChildLayer( rLayer
);
1067 ShapeAttributeLayerSharedPtr
DrawShape::getTopmostAttributeLayer() const
1069 return mpAttributeLayer
;
1072 void DrawShape::setVisibility( bool bVisible
)
1074 if( mbIsVisible
!= bVisible
)
1076 mbIsVisible
= bVisible
;
1077 mbForceUpdate
= true;
1081 const DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier() const
1086 DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier()
1091 DocTreeNode
DrawShape::getSubsetNode() const
1093 // forward to delegate
1094 return maSubsetting
.getSubsetNode();
1097 AttributableShapeSharedPtr
DrawShape::getSubset( const DocTreeNode
& rTreeNode
) const
1099 // forward to delegate
1100 return maSubsetting
.getSubsetShape( rTreeNode
);
1103 bool DrawShape::createSubset( AttributableShapeSharedPtr
& o_rSubset
,
1104 const DocTreeNode
& rTreeNode
)
1106 // subset shape already created for this DocTreeNode?
1107 AttributableShapeSharedPtr
pSubset( maSubsetting
.getSubsetShape( rTreeNode
) );
1109 // when true, this method has created a new subset
1111 bool bNewlyCreated( false );
1115 o_rSubset
= pSubset
;
1117 // reusing existing subset
1121 // not yet created, init entry
1122 o_rSubset
.reset( new DrawShape( *this,
1124 // TODO(Q3): That's a
1126 // that start and end
1127 // index will always
1128 // be less than 65535
1130 rTreeNode
.getStartIndex()/double(SAL_MAX_INT16
) ));
1132 bNewlyCreated
= true; // subset newly created
1135 // always register shape at DrawShapeSubsetting, to keep
1136 // refcount up-to-date
1137 maSubsetting
.addSubsetShape( o_rSubset
);
1139 // flush bounds cache
1140 maCurrentShapeUnitBounds
.reset();
1142 return bNewlyCreated
;
1145 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr
& rShape
)
1147 // flush bounds cache
1148 maCurrentShapeUnitBounds
.reset();
1150 // forward to delegate
1151 if( maSubsetting
.revokeSubsetShape( rShape
) )
1153 // force redraw, our content has possibly changed (as
1154 // one of the subsets now display within our shape
1156 mbForceUpdate
= true;
1158 // #i47428# TEMP FIX: synchronize visibility of subset
1161 // TODO(F3): Remove here, and implement
1162 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1163 // additional level of indirection: create a
1164 // persistent subset, containing all text/only the
1165 // background respectively. From _that_ object,
1166 // generate the temporary character subset shapes.
1167 const ShapeAttributeLayerSharedPtr
& rAttrLayer(
1168 rShape
->getTopmostAttributeLayer() );
1170 rAttrLayer
->isVisibilityValid() &&
1171 rAttrLayer
->getVisibility() != isVisible() )
1173 const bool bVisibility( rAttrLayer
->getVisibility() );
1175 // visibilities differ - adjust ours, then
1176 if( mpAttributeLayer
)
1177 mpAttributeLayer
->setVisibility( bVisibility
);
1179 mbIsVisible
= bVisibility
;
1190 sal_Int32
DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1192 return maSubsetting
.getNumberOfTreeNodes( eNodeType
);
1195 DocTreeNode
DrawShape::getTreeNode( sal_Int32 nNodeIndex
,
1196 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1198 if ( hasHyperlinks())
1200 prepareHyperlinkIndices();
1203 return maSubsetting
.getTreeNode( nNodeIndex
, eNodeType
);
1206 sal_Int32
DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode
& rParentNode
,
1207 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1209 return maSubsetting
.getNumberOfSubsetTreeNodes( rParentNode
, eNodeType
);
1212 DocTreeNode
DrawShape::getSubsetTreeNode( const DocTreeNode
& rParentNode
,
1213 sal_Int32 nNodeIndex
,
1214 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1216 return maSubsetting
.getSubsetTreeNode( rParentNode
, nNodeIndex
, eNodeType
);
1221 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */