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 <sal/log.hxx>
23 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <vcl/metaact.hxx>
26 #include <vcl/gdimtf.hxx>
27 #include <vcl/graph.hxx>
29 #include <basegfx/numeric/ftools.hxx>
31 #include <com/sun/star/drawing/TextAnimationKind.hpp>
33 #include <comphelper/scopeguard.hxx>
39 #include "drawshapesubsetting.hxx"
40 #include "drawshape.hxx"
41 #include <eventqueue.hxx>
42 #include <wakeupevent.hxx>
43 #include <subsettableshapemanager.hxx>
44 #include "intrinsicanimationactivity.hxx"
46 #include "gdimtftools.hxx"
47 #include "drawinglayeranimation.hxx"
49 using namespace ::com::sun::star
;
52 namespace slideshow::internal
59 GDIMetaFileSharedPtr
const & DrawShape::forceScrollTextMetaFile()
61 if ((mnCurrMtfLoadFlags
& MTF_LOAD_SCROLL_TEXT_MTF
) != MTF_LOAD_SCROLL_TEXT_MTF
)
63 // reload with added flags:
64 mnCurrMtfLoadFlags
|= MTF_LOAD_SCROLL_TEXT_MTF
;
65 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(mxShape
, uno::UNO_QUERY
),
66 mxPage
, mnCurrMtfLoadFlags
,
70 mpCurrMtf
= std::make_shared
<GDIMetaFile
>();
72 // TODO(F1): Currently, the scroll metafile will
73 // never contain any verbose text comments. Thus,
74 // can only display the full mtf content, no
76 maSubsetting
.reset( mpCurrMtf
);
78 // adapt maBounds. the requested scroll text metafile
79 // will typically have dimension different from the
81 ::basegfx::B2DRectangle aScrollRect
, aPaintRect
;
82 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect
,
85 "DrawShape::forceScrollTextMetaFile(): Could "
86 "not extract scroll anim rectangles from mtf" );
88 // take the larger one of the two rectangles (that
89 // should be the bound rect of the retrieved
91 if( aScrollRect
.isInside( aPaintRect
) )
92 maBounds
= aScrollRect
;
94 maBounds
= aPaintRect
;
99 void DrawShape::updateStateIds() const
101 // Update the states, we've just redrawn or created a new
103 if( mpAttributeLayer
)
105 mnAttributeTransformationState
= mpAttributeLayer
->getTransformationState();
106 mnAttributeClipState
= mpAttributeLayer
->getClipState();
107 mnAttributeAlphaState
= mpAttributeLayer
->getAlphaState();
108 mnAttributePositionState
= mpAttributeLayer
->getPositionState();
109 mnAttributeContentState
= mpAttributeLayer
->getContentState();
110 mnAttributeVisibilityState
= mpAttributeLayer
->getVisibilityState();
114 ViewShape::RenderArgs
DrawShape::getViewRenderArgs() const
116 return ViewShape::RenderArgs(
120 getActualUnitShapeBounds(),
122 maSubsetting
.getActiveSubsets(),
126 bool DrawShape::implRender( UpdateFlags nUpdateFlags
) const
128 SAL_INFO( "slideshow", "::presentation::internal::DrawShape::implRender()" );
129 SAL_INFO( "slideshow", "::presentation::internal::DrawShape: 0x" << std::hex
<< this );
131 // will perform the update now, clear update-enforcing
133 mbForceUpdate
= false;
134 mbAttributeLayerRevoked
= false;
136 ENSURE_OR_RETURN_FALSE( !maViewShapes
.empty(),
137 "DrawShape::implRender(): render called on DrawShape without views" );
139 if( maBounds
.isEmpty() )
141 // zero-sized shapes are effectively invisible,
142 // thus, we save us the rendering...
146 // redraw all view shapes, by calling their update() method
147 ViewShape::RenderArgs
renderArgs( getViewRenderArgs() );
148 bool bVisible
= isVisible();
149 if( ::std::count_if( maViewShapes
.begin(),
151 [this, &bVisible
, &renderArgs
, &nUpdateFlags
]
152 ( const ViewShapeSharedPtr
& pShape
)
153 { return pShape
->update( this->mpCurrMtf
,
157 != static_cast<ViewShapeVector::difference_type
>(maViewShapes
.size()) )
159 // at least one of the ViewShape::update() calls did return
160 // false - update failed on at least one ViewLayer
164 // successfully redrawn - update state IDs to detect next changes
170 UpdateFlags
DrawShape::getUpdateFlags() const
172 // default: update nothing, unless ShapeAttributeStack
173 // tells us below, or if the attribute layer was revoked
174 UpdateFlags
nUpdateFlags(UpdateFlags::NONE
);
176 // possibly the whole shape content changed
177 if( mbAttributeLayerRevoked
)
178 nUpdateFlags
= UpdateFlags::Content
;
181 // determine what has to be updated
184 // do we have an attribute layer?
185 if( mpAttributeLayer
)
187 // Prevent nUpdateFlags to be modified when the shape is not
188 // visible, except when it just was hidden.
189 if (mpAttributeLayer
->getVisibility()
190 || mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
192 if (mpAttributeLayer
->getVisibilityState() != mnAttributeVisibilityState
)
194 // Change of the visibility state is mapped to
195 // content change because when the visibility
196 // changes then usually a sprite is shown or hidden
197 // and the background under has to be painted once.
198 nUpdateFlags
|= UpdateFlags::Content
;
201 // TODO(P1): This can be done without conditional branching.
203 if( mpAttributeLayer
->getPositionState() != mnAttributePositionState
)
205 nUpdateFlags
|= UpdateFlags::Position
;
207 if( mpAttributeLayer
->getAlphaState() != mnAttributeAlphaState
)
209 nUpdateFlags
|= UpdateFlags::Alpha
;
211 if( mpAttributeLayer
->getClipState() != mnAttributeClipState
)
213 nUpdateFlags
|= UpdateFlags::Clip
;
215 if( mpAttributeLayer
->getTransformationState() != mnAttributeTransformationState
)
217 nUpdateFlags
|= UpdateFlags::Transformation
;
219 if( mpAttributeLayer
->getContentState() != mnAttributeContentState
)
221 nUpdateFlags
|= UpdateFlags::Content
;
229 ::basegfx::B2DRectangle
DrawShape::getActualUnitShapeBounds() const
231 ENSURE_OR_THROW( !maViewShapes
.empty(),
232 "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
234 const VectorOfDocTreeNodes
& rSubsets(
235 maSubsetting
.getActiveSubsets() );
237 const ::basegfx::B2DRectangle
aDefaultBounds( 0.0,0.0,1.0,1.0 );
239 // perform the cheapest check first
240 if( rSubsets
.empty() )
242 // if subset contains the whole shape, no need to call
243 // the somewhat expensive bound calculation, since as
244 // long as the subset is empty, this branch will be
246 return aDefaultBounds
;
250 OSL_ENSURE( rSubsets
.size() != 1 ||
251 !rSubsets
.front().isEmpty(),
252 "DrawShape::getActualUnitShapeBounds() expects a "
253 "_non-empty_ subset vector for a subsetted shape!" );
255 // are the cached bounds still valid?
256 if( !maCurrentShapeUnitBounds
)
258 // no, (re)generate them
259 // =====================
261 // setup cached values to defaults (might fail to
262 // retrieve true bounds below)
263 maCurrentShapeUnitBounds
= aDefaultBounds
;
265 // TODO(P2): the subset of the master shape (that from
266 // which the subsets are subtracted) changes
267 // relatively often (every time a subset shape is
268 // added or removed). Maybe we should exclude it here,
269 // always assuming full bounds?
271 ::cppcanvas::CanvasSharedPtr
pDestinationCanvas(
272 maViewShapes
.front()->getViewLayer()->getCanvas() );
274 // TODO(Q2): Although this _is_ currently
275 // view-agnostic, it might not stay like
276 // that. Maybe this method should again be moved
278 ::cppcanvas::RendererSharedPtr
pRenderer(
279 maViewShapes
.front()->getRenderer(
280 pDestinationCanvas
, mpCurrMtf
, mpAttributeLayer
) );
282 // If we cannot not prefetch, be defensive and assume
286 // temporarily, switch total transformation to identity
287 // (need the bounds in the [0,1]x[0,1] unit coordinate
289 ::basegfx::B2DHomMatrix aEmptyTransformation
;
291 ::basegfx::B2DHomMatrix
aOldTransform( pDestinationCanvas
->getTransformation() );
292 pDestinationCanvas
->setTransformation( aEmptyTransformation
);
293 pRenderer
->setTransformation( aEmptyTransformation
);
295 // restore old transformation when leaving the scope
296 const ::comphelper::ScopeGuard
aGuard(
297 [&pDestinationCanvas
, &aOldTransform
]()
298 { return pDestinationCanvas
->setTransformation( aOldTransform
); } );
301 // retrieve bounds for subset of whole metafile
304 ::basegfx::B2DRange aTotalBounds
;
306 // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
308 for( const auto& rDocTreeNode
: rSubsets
)
309 aTotalBounds
.expand( pRenderer
->getSubsetArea(
310 rDocTreeNode
.getStartIndex(),
311 rDocTreeNode
.getEndIndex() ) );
313 OSL_ENSURE( aTotalBounds
.getMinX() >= -0.1 &&
314 aTotalBounds
.getMinY() >= -0.1 &&
315 aTotalBounds
.getMaxX() <= 1.1 &&
316 aTotalBounds
.getMaxY() <= 1.1,
317 "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
319 // really make sure no shape appears larger than its
320 // original bounds (there _are_ some pathologic cases,
321 // especially when imported from PPT, that have
322 // e.g. obscenely large polygon bounds)
323 aTotalBounds
.intersect(
324 ::basegfx::B2DRange( 0.0, 0.0,
327 maCurrentShapeUnitBounds
= aTotalBounds
;
331 return *maCurrentShapeUnitBounds
;
335 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
336 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
339 const SlideShowContext
& rContext
) :
341 mxPage( xContainingPage
),
342 maAnimationFrames(), // empty, we don't have no intrinsic animation
345 mnCurrMtfLoadFlags( bForeignSource
346 ? MTF_LOAD_FOREIGN_SOURCE
: MTF_LOAD_NONE
),
347 maCurrentShapeUnitBounds(),
348 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
349 maBounds( getAPIShapeBounds( xShape
) ),
351 mpIntrinsicAnimationActivity(),
352 mnAttributeTransformationState(0),
353 mnAttributeClipState(0),
354 mnAttributeAlphaState(0),
355 mnAttributePositionState(0),
356 mnAttributeContentState(0),
357 mnAttributeVisibilityState(0),
359 mxComponentContext( rContext
.mxComponentContext
),
360 maHyperlinkIndices(),
361 maHyperlinkRegions(),
363 mnIsAnimatedCount(0),
364 mnAnimationLoopCount(0),
366 mbForceUpdate( false ),
367 mbAttributeLayerRevoked( false ),
368 mbDrawingLayerAnim( false )
370 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
371 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
373 // check for drawing layer animations:
374 drawing::TextAnimationKind eKind
= drawing::TextAnimationKind_NONE
;
375 uno::Reference
<beans::XPropertySet
> xPropSet( mxShape
,
378 getPropertyValue( eKind
, xPropSet
,
379 "TextAnimationKind" );
380 mbDrawingLayerAnim
= (eKind
!= drawing::TextAnimationKind_NONE
);
382 // must NOT be called from within initializer list, uses
383 // state from mnCurrMtfLoadFlags!
384 mpCurrMtf
= getMetaFile(uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY
),
385 xContainingPage
, mnCurrMtfLoadFlags
,
386 mxComponentContext
);
388 mpCurrMtf
= std::make_shared
<GDIMetaFile
>();
390 maSubsetting
.reset( mpCurrMtf
);
392 prepareHyperlinkIndices();
395 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
396 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
398 const Graphic
& rGraphic
,
399 const SlideShowContext
& rContext
) :
401 mxPage( xContainingPage
),
405 mnCurrMtfLoadFlags( MTF_LOAD_NONE
),
406 maCurrentShapeUnitBounds(),
407 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
408 maBounds( getAPIShapeBounds( xShape
) ),
410 mpIntrinsicAnimationActivity(),
411 mnAttributeTransformationState(0),
412 mnAttributeClipState(0),
413 mnAttributeAlphaState(0),
414 mnAttributePositionState(0),
415 mnAttributeContentState(0),
416 mnAttributeVisibilityState(0),
418 mxComponentContext( rContext
.mxComponentContext
),
419 maHyperlinkIndices(),
420 maHyperlinkRegions(),
422 mnIsAnimatedCount(0),
423 mnAnimationLoopCount(0),
425 mbForceUpdate( false ),
426 mbAttributeLayerRevoked( false ),
427 mbDrawingLayerAnim( false )
429 ENSURE_OR_THROW( rGraphic
.IsAnimated(),
430 "DrawShape::DrawShape(): Graphic is no animation" );
432 getAnimationFromGraphic( maAnimationFrames
,
433 mnAnimationLoopCount
,
436 ENSURE_OR_THROW( !maAnimationFrames
.empty() &&
437 maAnimationFrames
.front().mpMtf
,
438 "DrawShape::DrawShape(): " );
439 mpCurrMtf
= maAnimationFrames
.front().mpMtf
;
441 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
442 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
443 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
446 DrawShape::DrawShape( const DrawShape
& rSrc
,
447 const DocTreeNode
& rTreeNode
,
449 mxShape( rSrc
.mxShape
),
450 mxPage( rSrc
.mxPage
),
451 maAnimationFrames(), // don't copy animations for subsets,
452 // only the current frame!
454 mpCurrMtf( rSrc
.mpCurrMtf
),
455 mnCurrMtfLoadFlags( rSrc
.mnCurrMtfLoadFlags
),
456 maCurrentShapeUnitBounds(),
458 maBounds( rSrc
.maBounds
),
460 mpIntrinsicAnimationActivity(),
461 mnAttributeTransformationState(0),
462 mnAttributeClipState(0),
463 mnAttributeAlphaState(0),
464 mnAttributePositionState(0),
465 mnAttributeContentState(0),
466 mnAttributeVisibilityState(0),
468 mxComponentContext( rSrc
.mxComponentContext
),
469 maHyperlinkIndices(),
470 maHyperlinkRegions(),
471 maSubsetting( rTreeNode
, mpCurrMtf
),
472 mnIsAnimatedCount(0),
473 mnAnimationLoopCount(0),
474 mbIsVisible( rSrc
.mbIsVisible
),
475 mbForceUpdate( false ),
476 mbAttributeLayerRevoked( false ),
477 mbDrawingLayerAnim( false )
479 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
480 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
482 // xxx todo: currently not implemented for subsetted shapes;
483 // would mean modifying set of hyperlink regions when
484 // subsetting text portions. N.B.: there's already an
485 // issue for this #i72828#
492 DrawShapeSharedPtr
DrawShape::create(
493 const uno::Reference
< drawing::XShape
>& xShape
,
494 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
497 const SlideShowContext
& rContext
)
499 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
505 if( pShape
->hasIntrinsicAnimation() )
507 OSL_ASSERT( pShape
->maAnimationFrames
.empty() );
508 if( pShape
->getNumberOfTreeNodes(
509 DocTreeNode::NodeType::LogicalParagraph
) > 0 )
511 pShape
->mpIntrinsicAnimationActivity
=
512 createDrawingLayerAnimActivity(
518 if( pShape
->hasHyperlinks() )
519 rContext
.mpSubsettableShapeManager
->addHyperlinkArea( pShape
);
524 DrawShapeSharedPtr
DrawShape::create(
525 const uno::Reference
< drawing::XShape
>& xShape
,
526 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
528 const Graphic
& rGraphic
,
529 const SlideShowContext
& rContext
)
531 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
537 if( pShape
->hasIntrinsicAnimation() )
539 OSL_ASSERT( !pShape
->maAnimationFrames
.empty() );
541 std::vector
<double> aTimeout
;
543 pShape
->maAnimationFrames
.begin(),
544 pShape
->maAnimationFrames
.end(),
545 std::back_insert_iterator
< std::vector
<double> >( aTimeout
),
546 std::mem_fn(&MtfAnimationFrame::getDuration
) );
548 WakeupEventSharedPtr pWakeupEvent
=
549 std::make_shared
<WakeupEvent
>( rContext
.mrEventQueue
.getTimer(),
550 rContext
.mrActivitiesQueue
);
552 ActivitySharedPtr pActivity
=
553 createIntrinsicAnimationActivity(
558 pShape
->mnAnimationLoopCount
);
560 pWakeupEvent
->setActivity( pActivity
);
561 pShape
->mpIntrinsicAnimationActivity
= pActivity
;
564 OSL_ENSURE( !pShape
->hasHyperlinks(),
565 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
570 DrawShape::~DrawShape()
574 // dispose intrinsic animation activity, else, it will
576 ActivitySharedPtr
pActivity( mpIntrinsicAnimationActivity
.lock() );
578 pActivity
->dispose();
580 catch (uno::Exception
const &)
582 DBG_UNHANDLED_EXCEPTION("slideshow");
586 uno::Reference
< drawing::XShape
> DrawShape::getXShape() const
591 void DrawShape::addViewLayer( const ViewLayerSharedPtr
& rNewLayer
,
595 if( ::std::any_of( maViewShapes
.begin(),
598 ( const ViewShapeSharedPtr
& pShape
)
599 { return rNewLayer
== pShape
->getViewLayer(); } ) )
601 // yes, nothing to do
605 ViewShapeSharedPtr pNewShape
= std::make_shared
<ViewShape
>( rNewLayer
);
607 maViewShapes
.push_back( pNewShape
);
609 // pass on animation state
610 if( mnIsAnimatedCount
)
612 for( int i
=0; i
<mnIsAnimatedCount
; ++i
)
613 pNewShape
->enterAnimationMode();
616 // render the Shape on the newly added ViewLayer
619 pNewShape
->update( mpCurrMtf
,
626 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr
& rLayer
)
628 const ViewShapeVector::iterator
aEnd( maViewShapes
.end() );
630 OSL_ENSURE( ::std::count_if(maViewShapes
.begin(),
633 ( const ViewShapeSharedPtr
& pShape
)
634 { return rLayer
== pShape
->getViewLayer(); } ) < 2,
635 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
637 ViewShapeVector::iterator aIter
;
639 if( (aIter
=::std::remove_if( maViewShapes
.begin(),
642 ( const ViewShapeSharedPtr
& pShape
)
643 { return rLayer
== pShape
->getViewLayer(); } ) ) == aEnd
)
645 // view layer seemingly was not added, failed
649 // actually erase from container
650 maViewShapes
.erase( aIter
, aEnd
);
655 void DrawShape::clearAllViewLayers()
657 maViewShapes
.clear();
660 bool DrawShape::update() const
668 return implRender( getUpdateFlags() );
672 bool DrawShape::render() const
674 // force redraw. Have to also pass on the update flags,
675 // because e.g. content update (regeneration of the
676 // metafile renderer) is normally not performed. A simple
677 // UpdateFlags::Force would only paint the metafile in its
679 return implRender( UpdateFlags::Force
| getUpdateFlags() );
682 bool DrawShape::isContentChanged() const
684 return mbForceUpdate
||
685 getUpdateFlags() != UpdateFlags::NONE
;
689 ::basegfx::B2DRectangle
DrawShape::getBounds() const
691 // little optimization: for non-modified shapes, we don't
692 // create an ShapeAttributeStack, and therefore also don't
694 return getShapePosSize( maBounds
,
698 ::basegfx::B2DRectangle
DrawShape::getDomBounds() const
703 ::basegfx::B2DRectangle
DrawShape::getUpdateArea() const
705 ::basegfx::B2DRectangle aBounds
;
707 // an already empty shape bound need no further
708 // treatment. In fact, any changes applied below would
709 // actually remove the special empty state, thus, don't
711 if( !maBounds
.isEmpty() )
713 basegfx::B2DRectangle
aUnitBounds(0.0,0.0,1.0,1.0);
715 if( !maViewShapes
.empty() )
716 aUnitBounds
= getActualUnitShapeBounds();
718 if( !aUnitBounds
.isEmpty() )
720 if( mpAttributeLayer
)
722 // calc actual shape area (in user coordinate
723 // space) from the transformation as given by the
724 // shape attribute layer
725 aBounds
= getShapeUpdateArea( aUnitBounds
,
726 getShapeTransformation( getBounds(),
732 // no attribute layer, thus, the true shape bounds
733 // can be directly derived from the XShape bound
735 aBounds
= getShapeUpdateArea( aUnitBounds
,
739 if( !maViewShapes
.empty() )
741 // determine border needed for antialiasing the shape
742 ::basegfx::B2DSize
aAABorder(0.0,0.0);
744 // for every view, get AA border and 'expand' aAABorder
746 for( const auto& rViewShape
: maViewShapes
)
748 const ::basegfx::B2DSize
rShapeBorder( rViewShape
->getAntialiasingBorder() );
750 aAABorder
.setX( ::std::max(
752 aAABorder
.getX() ) );
753 aAABorder
.setY( ::std::max(
755 aAABorder
.getY() ) );
758 // add calculated AA border to aBounds
759 aBounds
= ::basegfx::B2DRectangle( aBounds
.getMinX() - aAABorder
.getX(),
760 aBounds
.getMinY() - aAABorder
.getY(),
761 aBounds
.getMaxX() + aAABorder
.getX(),
762 aBounds
.getMaxY() + aAABorder
.getY() );
770 bool DrawShape::isVisible() const
772 bool bIsVisible( mbIsVisible
);
774 if( mpAttributeLayer
)
776 // check whether visibility and alpha are not default
777 // (mpAttributeLayer->isVisibilityValid() returns true
778 // then): bVisible becomes true, if shape visibility
779 // is on and alpha is not 0.0 (fully transparent)
780 if( mpAttributeLayer
->isVisibilityValid() )
781 bIsVisible
= mpAttributeLayer
->getVisibility();
783 // only touch bIsVisible, if the shape is still
784 // visible - if getVisibility already made us
785 // invisible, no alpha value will make us appear
787 if( bIsVisible
&& mpAttributeLayer
->isAlphaValid() )
788 bIsVisible
= !::basegfx::fTools::equalZero( mpAttributeLayer
->getAlpha() );
794 double DrawShape::getPriority() const
799 bool DrawShape::isBackgroundDetached() const
801 return mnIsAnimatedCount
> 0;
804 bool DrawShape::hasIntrinsicAnimation() const
806 return (!maAnimationFrames
.empty() || mbDrawingLayerAnim
);
809 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame
)
811 ENSURE_OR_RETURN_VOID( nCurrFrame
< maAnimationFrames
.size(),
812 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
814 if( mnCurrFrame
!= nCurrFrame
)
816 mnCurrFrame
= nCurrFrame
;
817 mpCurrMtf
= maAnimationFrames
[ mnCurrFrame
].mpMtf
;
818 mbForceUpdate
= true;
823 void DrawShape::prepareHyperlinkIndices() const
825 if ( !maHyperlinkIndices
.empty())
827 maHyperlinkIndices
.clear();
828 maHyperlinkRegions
.clear();
831 sal_Int32 nIndex
= 0;
832 for ( MetaAction
* pCurrAct
= mpCurrMtf
->FirstAction();
833 pCurrAct
!= nullptr; pCurrAct
= mpCurrMtf
->NextAction() )
835 if (pCurrAct
->GetType() == MetaActionType::COMMENT
) {
836 MetaCommentAction
* pAct
=
837 static_cast<MetaCommentAction
*>(pCurrAct
);
838 // skip comment if not a special XTEXT comment
839 if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
840 // e.g. date field doesn't have data!
841 // currently assuming that only url field, this is
842 // somehow fragile! xxx todo if possible
843 pAct
->GetData() != nullptr &&
844 pAct
->GetDataSize() > 0)
846 if (!maHyperlinkIndices
.empty() &&
847 maHyperlinkIndices
.back().second
== -1) {
848 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
849 maHyperlinkIndices
.pop_back();
850 maHyperlinkRegions
.pop_back();
852 maHyperlinkIndices
.emplace_back( nIndex
+ 1,
853 -1 /* to be filled below */ );
854 maHyperlinkRegions
.emplace_back(
855 basegfx::B2DRectangle(),
857 reinterpret_cast<sal_Unicode
const*>(
859 pAct
->GetDataSize() / sizeof(sal_Unicode
) )
862 else if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
863 // pending end is expected:
864 !maHyperlinkIndices
.empty() &&
865 maHyperlinkIndices
.back().second
== -1)
867 maHyperlinkIndices
.back().second
= nIndex
;
872 nIndex
+= getNextActionOffset(pCurrAct
);
874 if (!maHyperlinkIndices
.empty() &&
875 maHyperlinkIndices
.back().second
== -1) {
876 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
877 maHyperlinkIndices
.pop_back();
878 maHyperlinkRegions
.pop_back();
880 OSL_ASSERT( maHyperlinkIndices
.size() == maHyperlinkRegions
.size());
883 bool DrawShape::hasHyperlinks() const
885 return ! maHyperlinkRegions
.empty();
888 HyperlinkArea::HyperlinkRegions
DrawShape::getHyperlinkRegions() const
890 OSL_ASSERT( !maViewShapes
.empty() );
893 return HyperlinkArea::HyperlinkRegions();
895 // late init, determine regions:
896 if( !maHyperlinkRegions
.empty() &&
897 !maViewShapes
.empty() &&
898 // region already inited?
899 maHyperlinkRegions
.front().first
.getWidth() == 0 &&
900 maHyperlinkRegions
.front().first
.getHeight() == 0 &&
901 maHyperlinkRegions
.size() == maHyperlinkIndices
.size() )
903 // TODO(Q2): Although this _is_ currently
904 // view-agnostic, it might not stay like that.
905 ViewShapeSharedPtr
const& pViewShape
= maViewShapes
.front();
906 cppcanvas::CanvasSharedPtr
const pCanvas(
907 pViewShape
->getViewLayer()->getCanvas() );
909 // reuse Renderer of first view shape:
910 cppcanvas::RendererSharedPtr
const pRenderer(
911 pViewShape
->getRenderer(
912 pCanvas
, mpCurrMtf
, mpAttributeLayer
) );
914 OSL_ASSERT( pRenderer
);
918 basegfx::B2DHomMatrix
const aOldTransform(
919 pCanvas
->getTransformation() );
920 basegfx::B2DHomMatrix aTransform
;
921 pCanvas
->setTransformation( aTransform
/* empty */ );
924 ::cppcanvas::Canvas
* pTmpCanvas
= pCanvas
.get();
925 comphelper::ScopeGuard
const resetOldTransformation(
926 [&aOldTransform
, &pTmpCanvas
]()
927 { return pTmpCanvas
->setTransformation( aOldTransform
); } );
929 aTransform
.scale( maBounds
.getWidth(),
930 maBounds
.getHeight() );
931 pRenderer
->setTransformation( aTransform
);
932 pRenderer
->setClip();
934 for( std::size_t pos
= maHyperlinkRegions
.size(); pos
--; )
937 HyperlinkIndexPair
const& rIndices
= maHyperlinkIndices
[pos
];
938 basegfx::B2DRectangle
const region(
939 pRenderer
->getSubsetArea( rIndices
.first
,
941 maHyperlinkRegions
[pos
].first
= region
;
946 // shift shape-relative hyperlink regions to
947 // slide-absolute position
949 HyperlinkRegions aTranslatedRegions
;
951 // increase capacity to same size as the container for
952 // shape-relative hyperlink regions to avoid reallocation
953 aTranslatedRegions
.reserve( maHyperlinkRegions
.size() );
954 const basegfx::B2DPoint
& rOffset(getBounds().getMinimum());
955 for( const auto& cp
: maHyperlinkRegions
)
957 basegfx::B2DRange
const& relRegion( cp
.first
);
958 aTranslatedRegions
.emplace_back(
960 relRegion
.getMinimum() + rOffset
,
961 relRegion
.getMaximum() + rOffset
),
965 return aTranslatedRegions
;
968 double DrawShape::getHyperlinkPriority() const
970 return getPriority();
974 // AnimatableShape methods
977 void DrawShape::enterAnimationMode()
979 OSL_ENSURE( !maViewShapes
.empty(),
980 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
982 if( mnIsAnimatedCount
== 0 )
984 // notify all ViewShapes, by calling their enterAnimationMode method.
985 // We're now entering animation mode
986 for( const auto& rViewShape
: maViewShapes
)
987 rViewShape
->enterAnimationMode();
993 void DrawShape::leaveAnimationMode()
995 OSL_ENSURE( !maViewShapes
.empty(),
996 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1000 if( mnIsAnimatedCount
== 0 )
1002 // notify all ViewShapes, by calling their leaveAnimationMode method.
1003 // we're now leaving animation mode
1004 for( const auto& rViewShape
: maViewShapes
)
1005 rViewShape
->leaveAnimationMode();
1010 // AttributableShape methods
1013 ShapeAttributeLayerSharedPtr
DrawShape::createAttributeLayer()
1015 // create new layer, with last as its new child
1016 mpAttributeLayer
= std::make_shared
<ShapeAttributeLayer
>( mpAttributeLayer
);
1018 // Update the local state ids to reflect those of the new layer.
1021 return mpAttributeLayer
;
1024 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr
& rLayer
)
1026 if( !mpAttributeLayer
)
1027 return false; // no layers
1029 if( mpAttributeLayer
== rLayer
)
1031 // it's the toplevel layer
1032 mpAttributeLayer
= mpAttributeLayer
->getChildLayer();
1034 // force content redraw, all state variables have
1036 mbAttributeLayerRevoked
= true;
1042 // pass on to the layer, to try its children
1043 return mpAttributeLayer
->revokeChildLayer( rLayer
);
1047 ShapeAttributeLayerSharedPtr
DrawShape::getTopmostAttributeLayer() const
1049 return mpAttributeLayer
;
1052 void DrawShape::setVisibility( bool bVisible
)
1054 if( mbIsVisible
!= bVisible
)
1056 mbIsVisible
= bVisible
;
1057 mbForceUpdate
= true;
1061 const DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier() const
1066 DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier()
1071 DocTreeNode
DrawShape::getSubsetNode() const
1073 // forward to delegate
1074 return maSubsetting
.getSubsetNode();
1077 AttributableShapeSharedPtr
DrawShape::getSubset( const DocTreeNode
& rTreeNode
) const
1079 // forward to delegate
1080 return maSubsetting
.getSubsetShape( rTreeNode
);
1083 bool DrawShape::createSubset( AttributableShapeSharedPtr
& o_rSubset
,
1084 const DocTreeNode
& rTreeNode
)
1086 // subset shape already created for this DocTreeNode?
1087 AttributableShapeSharedPtr
pSubset( maSubsetting
.getSubsetShape( rTreeNode
) );
1089 // when true, this method has created a new subset
1091 bool bNewlyCreated( false );
1095 o_rSubset
= pSubset
;
1097 // reusing existing subset
1101 // not yet created, init entry
1102 o_rSubset
.reset( new DrawShape( *this,
1104 // TODO(Q3): That's a
1106 // that start and end
1107 // index will always
1108 // be less than 65535
1110 rTreeNode
.getStartIndex()/double(SAL_MAX_INT16
) ));
1112 bNewlyCreated
= true; // subset newly created
1115 // always register shape at DrawShapeSubsetting, to keep
1116 // refcount up-to-date
1117 maSubsetting
.addSubsetShape( o_rSubset
);
1119 // flush bounds cache
1120 maCurrentShapeUnitBounds
.reset();
1122 return bNewlyCreated
;
1125 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr
& rShape
)
1127 // flush bounds cache
1128 maCurrentShapeUnitBounds
.reset();
1130 // forward to delegate
1131 if( maSubsetting
.revokeSubsetShape( rShape
) )
1133 // force redraw, our content has possibly changed (as
1134 // one of the subsets now display within our shape
1136 mbForceUpdate
= true;
1138 // #i47428# TEMP FIX: synchronize visibility of subset
1141 // TODO(F3): Remove here, and implement
1142 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1143 // additional level of indirection: create a
1144 // persistent subset, containing all text/only the
1145 // background respectively. From _that_ object,
1146 // generate the temporary character subset shapes.
1147 const ShapeAttributeLayerSharedPtr
& rAttrLayer(
1148 rShape
->getTopmostAttributeLayer() );
1150 rAttrLayer
->isVisibilityValid() &&
1151 rAttrLayer
->getVisibility() != isVisible() )
1153 const bool bVisibility( rAttrLayer
->getVisibility() );
1155 // visibilities differ - adjust ours, then
1156 if( mpAttributeLayer
)
1157 mpAttributeLayer
->setVisibility( bVisibility
);
1159 mbIsVisible
= bVisibility
;
1170 sal_Int32
DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1172 return maSubsetting
.getNumberOfTreeNodes( eNodeType
);
1175 DocTreeNode
DrawShape::getTreeNode( sal_Int32 nNodeIndex
,
1176 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1178 if ( hasHyperlinks())
1180 prepareHyperlinkIndices();
1183 return maSubsetting
.getTreeNode( nNodeIndex
, eNodeType
);
1186 sal_Int32
DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode
& rParentNode
,
1187 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1189 return maSubsetting
.getNumberOfSubsetTreeNodes( rParentNode
, eNodeType
);
1192 DocTreeNode
DrawShape::getSubsetTreeNode( const DocTreeNode
& rParentNode
,
1193 sal_Int32 nNodeIndex
,
1194 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1196 return maSubsetting
.getSubsetTreeNode( rParentNode
, nNodeIndex
, eNodeType
);
1200 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */