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/frame/XModel.hpp>
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 mpCurrMtf
.reset( new GDIMetaFile
);
86 mnCurrMtfLoadFlags
|= MTF_LOAD_SCROLL_TEXT_MTF
;
88 uno::Reference
<lang::XComponent
>(mxShape
, uno::UNO_QUERY
),
89 mxPage
, *mpCurrMtf
, mnCurrMtfLoadFlags
,
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( int 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 int DrawShape::getUpdateFlags() const
192 // default: update nothing, unless ShapeAttributeStack
193 // tells us below, or if the attribute layer was revoked
194 int nUpdateFlags(ViewShape::NONE
);
196 // possibly the whole shape content changed
197 if( mbAttributeLayerRevoked
)
198 nUpdateFlags
= ViewShape::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
|= ViewShape::CONTENT
;
221 // TODO(P1): This can be done without conditional branching.
223 if( mpAttributeLayer
->getPositionState() != mnAttributePositionState
)
225 nUpdateFlags
|= ViewShape::POSITION
;
227 if( mpAttributeLayer
->getAlphaState() != mnAttributeAlphaState
)
229 nUpdateFlags
|= ViewShape::ALPHA
;
231 if( mpAttributeLayer
->getClipState() != mnAttributeClipState
)
233 nUpdateFlags
|= ViewShape::CLIP
;
235 if( mpAttributeLayer
->getTransformationState() != mnAttributeTransformationState
)
237 nUpdateFlags
|= ViewShape::TRANSFORMATION
;
239 if( mpAttributeLayer
->getContentState() != mnAttributeContentState
)
241 nUpdateFlags
|= ViewShape::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),
385 meCycleMode(CYCLE_LOOP
),
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
.reset( new GDIMetaFile
);
407 uno::Reference
<lang::XComponent
>(xShape
, uno::UNO_QUERY
),
408 xContainingPage
, *mpCurrMtf
, mnCurrMtfLoadFlags
,
409 mxComponentContext
);
410 ENSURE_OR_THROW( mpCurrMtf
,
411 "DrawShape::DrawShape(): Invalid metafile" );
412 maSubsetting
.reset( mpCurrMtf
);
414 prepareHyperlinkIndices();
417 DrawShape::DrawShape( const uno::Reference
< drawing::XShape
>& xShape
,
418 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
420 const Graphic
& rGraphic
,
421 const SlideShowContext
& rContext
) :
423 mxPage( xContainingPage
),
427 mnCurrMtfLoadFlags( MTF_LOAD_NONE
),
428 maCurrentShapeUnitBounds(),
429 mnPriority( nPrio
), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
430 maBounds( getAPIShapeBounds( xShape
) ),
432 mpIntrinsicAnimationActivity(),
433 mnAttributeTransformationState(0),
434 mnAttributeClipState(0),
435 mnAttributeAlphaState(0),
436 mnAttributePositionState(0),
437 mnAttributeContentState(0),
438 mnAttributeVisibilityState(0),
440 mxComponentContext( rContext
.mxComponentContext
),
441 maHyperlinkIndices(),
442 maHyperlinkRegions(),
444 mnIsAnimatedCount(0),
445 mnAnimationLoopCount(0),
446 meCycleMode(CYCLE_LOOP
),
448 mbForceUpdate( false ),
449 mbAttributeLayerRevoked( false ),
450 mbDrawingLayerAnim( false )
452 ENSURE_OR_THROW( rGraphic
.IsAnimated(),
453 "DrawShape::DrawShape(): Graphic is no animation" );
455 getAnimationFromGraphic( maAnimationFrames
,
456 mnAnimationLoopCount
,
460 ENSURE_OR_THROW( !maAnimationFrames
.empty() &&
461 maAnimationFrames
.front().mpMtf
,
462 "DrawShape::DrawShape(): " );
463 mpCurrMtf
= maAnimationFrames
.front().mpMtf
;
465 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
466 ENSURE_OR_THROW( mxPage
.is(), "DrawShape::DrawShape(): Invalid containing page" );
467 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
470 DrawShape::DrawShape( const DrawShape
& rSrc
,
471 const DocTreeNode
& rTreeNode
,
473 mxShape( rSrc
.mxShape
),
474 mxPage( rSrc
.mxPage
),
475 maAnimationFrames(), // don't copy animations for subsets,
476 // only the current frame!
478 mpCurrMtf( rSrc
.mpCurrMtf
),
479 mnCurrMtfLoadFlags( rSrc
.mnCurrMtfLoadFlags
),
480 maCurrentShapeUnitBounds(),
482 maBounds( rSrc
.maBounds
),
484 mpIntrinsicAnimationActivity(),
485 mnAttributeTransformationState(0),
486 mnAttributeClipState(0),
487 mnAttributeAlphaState(0),
488 mnAttributePositionState(0),
489 mnAttributeContentState(0),
490 mnAttributeVisibilityState(0),
492 mxComponentContext( rSrc
.mxComponentContext
),
493 maHyperlinkIndices(),
494 maHyperlinkRegions(),
495 maSubsetting( rTreeNode
, mpCurrMtf
),
496 mnIsAnimatedCount(0),
497 mnAnimationLoopCount(0),
498 meCycleMode(CYCLE_LOOP
),
499 mbIsVisible( rSrc
.mbIsVisible
),
500 mbForceUpdate( false ),
501 mbAttributeLayerRevoked( false ),
502 mbDrawingLayerAnim( false )
504 ENSURE_OR_THROW( mxShape
.is(), "DrawShape::DrawShape(): Invalid XShape" );
505 ENSURE_OR_THROW( mpCurrMtf
, "DrawShape::DrawShape(): Invalid metafile" );
507 // xxx todo: currently not implemented for subsetted shapes;
508 // would mean modifying set of hyperlink regions when
509 // subsetting text portions. N.B.: there's already an
510 // issue for this #i72828#
517 DrawShapeSharedPtr
DrawShape::create(
518 const uno::Reference
< drawing::XShape
>& xShape
,
519 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
522 const SlideShowContext
& rContext
)
524 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
530 if( pShape
->hasIntrinsicAnimation() )
532 OSL_ASSERT( pShape
->maAnimationFrames
.empty() );
533 if( pShape
->getNumberOfTreeNodes(
534 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH
) > 0 )
536 pShape
->mpIntrinsicAnimationActivity
=
537 createDrawingLayerAnimActivity(
543 if( pShape
->hasHyperlinks() )
544 rContext
.mpSubsettableShapeManager
->addHyperlinkArea( pShape
);
549 DrawShapeSharedPtr
DrawShape::create(
550 const uno::Reference
< drawing::XShape
>& xShape
,
551 const uno::Reference
< drawing::XDrawPage
>& xContainingPage
,
553 const Graphic
& rGraphic
,
554 const SlideShowContext
& rContext
)
556 DrawShapeSharedPtr
pShape( new DrawShape(xShape
,
562 if( pShape
->hasIntrinsicAnimation() )
564 OSL_ASSERT( !pShape
->maAnimationFrames
.empty() );
566 std::vector
<double> aTimeout
;
568 pShape
->maAnimationFrames
.begin(),
569 pShape
->maAnimationFrames
.end(),
570 std::back_insert_iterator
< std::vector
<double> >( aTimeout
),
571 std::mem_fn(&MtfAnimationFrame::getDuration
) );
573 WakeupEventSharedPtr
pWakeupEvent(
574 new WakeupEvent( rContext
.mrEventQueue
.getTimer(),
575 rContext
.mrActivitiesQueue
) );
577 ActivitySharedPtr pActivity
=
578 createIntrinsicAnimationActivity(
583 pShape
->mnAnimationLoopCount
,
584 pShape
->meCycleMode
);
586 pWakeupEvent
->setActivity( pActivity
);
587 pShape
->mpIntrinsicAnimationActivity
= pActivity
;
590 OSL_ENSURE( !pShape
->hasHyperlinks(),
591 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
596 DrawShape::~DrawShape()
600 // dispose intrinsic animation activity, else, it will
602 ActivitySharedPtr
pActivity( mpIntrinsicAnimationActivity
.lock() );
604 pActivity
->dispose();
606 catch (uno::Exception
&)
608 SAL_WARN( "slideshow", "" << comphelper::anyToString(cppu::getCaughtException() ) );
612 uno::Reference
< drawing::XShape
> DrawShape::getXShape() const
617 void DrawShape::addViewLayer( const ViewLayerSharedPtr
& rNewLayer
,
621 if( ::std::any_of( maViewShapes
.begin(),
624 ( const ViewShapeSharedPtr
& pShape
)
625 { return rNewLayer
== pShape
->getViewLayer(); } ) )
627 // yes, nothing to do
631 ViewShapeSharedPtr
pNewShape( new ViewShape( rNewLayer
) );
633 maViewShapes
.push_back( pNewShape
);
635 // pass on animation state
636 if( mnIsAnimatedCount
)
638 for( int i
=0; i
<mnIsAnimatedCount
; ++i
)
639 pNewShape
->enterAnimationMode();
642 // render the Shape on the newly added ViewLayer
645 pNewShape
->update( mpCurrMtf
,
652 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr
& rLayer
)
654 const ViewShapeVector::iterator
aEnd( maViewShapes
.end() );
656 OSL_ENSURE( ::std::count_if(maViewShapes
.begin(),
659 ( const ViewShapeSharedPtr
& pShape
)
660 { return rLayer
== pShape
->getViewLayer(); } ) < 2,
661 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
663 ViewShapeVector::iterator aIter
;
665 if( (aIter
=::std::remove_if( maViewShapes
.begin(),
668 ( const ViewShapeSharedPtr
& pShape
)
669 { return rLayer
== pShape
->getViewLayer(); } ) ) == aEnd
)
671 // view layer seemingly was not added, failed
675 // actually erase from container
676 maViewShapes
.erase( aIter
, aEnd
);
681 void DrawShape::clearAllViewLayers()
683 maViewShapes
.clear();
686 bool DrawShape::update() const
694 return implRender( getUpdateFlags() );
698 bool DrawShape::render() const
700 // force redraw. Have to also pass on the update flags,
701 // because e.g. content update (regeneration of the
702 // metafile renderer) is normally not performed. A simple
703 // ViewShape::FORCE would only paint the metafile in its
705 return implRender( ViewShape::FORCE
| getUpdateFlags() );
708 bool DrawShape::isContentChanged() const
710 return mbForceUpdate
||
711 getUpdateFlags() != ViewShape::NONE
;
715 ::basegfx::B2DRectangle
DrawShape::getBounds() const
717 // little optimization: for non-modified shapes, we don't
718 // create an ShapeAttributeStack, and therefore also don't
720 return getShapePosSize( maBounds
,
724 ::basegfx::B2DRectangle
DrawShape::getDomBounds() const
729 ::basegfx::B2DRectangle
DrawShape::getUpdateArea() const
731 ::basegfx::B2DRectangle aBounds
;
733 // an already empty shape bound need no further
734 // treatment. In fact, any changes applied below would
735 // actually remove the special empty state, thus, don't
737 if( !maBounds
.isEmpty() )
739 basegfx::B2DRectangle
aUnitBounds(0.0,0.0,1.0,1.0);
741 if( !maViewShapes
.empty() )
742 aUnitBounds
= getActualUnitShapeBounds();
744 if( !aUnitBounds
.isEmpty() )
746 if( mpAttributeLayer
)
748 // calc actual shape area (in user coordinate
749 // space) from the transformation as given by the
750 // shape attribute layer
751 aBounds
= getShapeUpdateArea( aUnitBounds
,
752 getShapeTransformation( getBounds(),
758 // no attribute layer, thus, the true shape bounds
759 // can be directly derived from the XShape bound
761 aBounds
= getShapeUpdateArea( aUnitBounds
,
765 if( !maViewShapes
.empty() )
767 // determine border needed for antialiasing the shape
768 ::basegfx::B2DSize
aAABorder(0.0,0.0);
770 // for every view, get AA border and 'expand' aAABorder
772 for( const auto& rViewShape
: maViewShapes
)
774 const ::basegfx::B2DSize
rShapeBorder( rViewShape
->getAntialiasingBorder() );
776 aAABorder
.setX( ::std::max(
778 aAABorder
.getX() ) );
779 aAABorder
.setY( ::std::max(
781 aAABorder
.getY() ) );
784 // add calculated AA border to aBounds
785 aBounds
= ::basegfx::B2DRectangle( aBounds
.getMinX() - aAABorder
.getX(),
786 aBounds
.getMinY() - aAABorder
.getY(),
787 aBounds
.getMaxX() + aAABorder
.getX(),
788 aBounds
.getMaxY() + aAABorder
.getY() );
796 bool DrawShape::isVisible() const
798 bool bIsVisible( mbIsVisible
);
800 if( mpAttributeLayer
)
802 // check whether visibility and alpha are not default
803 // (mpAttributeLayer->isVisibilityValid() returns true
804 // then): bVisible becomes true, if shape visibility
805 // is on and alpha is not 0.0 (fully transparent)
806 if( mpAttributeLayer
->isVisibilityValid() )
807 bIsVisible
= mpAttributeLayer
->getVisibility();
809 // only touch bIsVisible, if the shape is still
810 // visible - if getVisibility already made us
811 // invisible, no alpha value will make us appear
813 if( bIsVisible
&& mpAttributeLayer
->isAlphaValid() )
814 bIsVisible
= !::basegfx::fTools::equalZero( mpAttributeLayer
->getAlpha() );
820 double DrawShape::getPriority() const
825 bool DrawShape::isBackgroundDetached() const
827 return mnIsAnimatedCount
> 0;
830 bool DrawShape::hasIntrinsicAnimation() const
832 return (!maAnimationFrames
.empty() || mbDrawingLayerAnim
);
835 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame
)
837 ENSURE_OR_RETURN_VOID( nCurrFrame
< maAnimationFrames
.size(),
838 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
840 if( mnCurrFrame
!= nCurrFrame
)
842 mnCurrFrame
= nCurrFrame
;
843 mpCurrMtf
= maAnimationFrames
[ mnCurrFrame
].mpMtf
;
844 mbForceUpdate
= true;
849 void DrawShape::prepareHyperlinkIndices() const
851 if ( !maHyperlinkIndices
.empty())
853 maHyperlinkIndices
.clear();
854 maHyperlinkRegions
.clear();
857 sal_Int32 nIndex
= 0;
858 for ( MetaAction
* pCurrAct
= mpCurrMtf
->FirstAction();
859 pCurrAct
!= nullptr; pCurrAct
= mpCurrMtf
->NextAction() )
861 if (pCurrAct
->GetType() == MetaActionType::COMMENT
) {
862 MetaCommentAction
* pAct
=
863 static_cast<MetaCommentAction
*>(pCurrAct
);
864 // skip comment if not a special XTEXT comment
865 if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
866 // e.g. date field doesn't have data!
867 // currently assuming that only url field, this is
868 // somehow fragile! xxx todo if possible
869 pAct
->GetData() != nullptr &&
870 pAct
->GetDataSize() > 0)
872 if (!maHyperlinkIndices
.empty() &&
873 maHyperlinkIndices
.back().second
== -1) {
874 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
875 maHyperlinkIndices
.pop_back();
876 maHyperlinkRegions
.pop_back();
878 maHyperlinkIndices
.push_back(
879 HyperlinkIndexPair( nIndex
+ 1,
880 -1 /* to be filled below */ ) );
881 maHyperlinkRegions
.push_back(
883 basegfx::B2DRectangle(),
885 reinterpret_cast<sal_Unicode
const*>(
887 pAct
->GetDataSize() / sizeof(sal_Unicode
) )
890 else if (pAct
->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
891 // pending end is expected:
892 !maHyperlinkIndices
.empty() &&
893 maHyperlinkIndices
.back().second
== -1)
895 maHyperlinkIndices
.back().second
= nIndex
;
900 nIndex
+= getNextActionOffset(pCurrAct
);
902 if (!maHyperlinkIndices
.empty() &&
903 maHyperlinkIndices
.back().second
== -1) {
904 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
905 maHyperlinkIndices
.pop_back();
906 maHyperlinkRegions
.pop_back();
908 OSL_ASSERT( maHyperlinkIndices
.size() == maHyperlinkRegions
.size());
911 bool DrawShape::hasHyperlinks() const
913 return ! maHyperlinkRegions
.empty();
916 HyperlinkArea::HyperlinkRegions
DrawShape::getHyperlinkRegions() const
918 OSL_ASSERT( !maViewShapes
.empty() );
921 return HyperlinkArea::HyperlinkRegions();
923 // late init, determine regions:
924 if( !maHyperlinkRegions
.empty() &&
925 !maViewShapes
.empty() &&
926 // region already inited?
927 maHyperlinkRegions
.front().first
.getWidth() == 0 &&
928 maHyperlinkRegions
.front().first
.getHeight() == 0 &&
929 maHyperlinkRegions
.size() == maHyperlinkIndices
.size() )
931 // TODO(Q2): Although this _is_ currently
932 // view-agnostic, it might not stay like that.
933 ViewShapeSharedPtr
const& pViewShape
= maViewShapes
.front();
934 cppcanvas::CanvasSharedPtr
const pCanvas(
935 pViewShape
->getViewLayer()->getCanvas() );
937 // reuse Renderer of first view shape:
938 cppcanvas::RendererSharedPtr
const pRenderer(
939 pViewShape
->getRenderer(
940 pCanvas
, mpCurrMtf
, mpAttributeLayer
) );
942 OSL_ASSERT( pRenderer
);
946 basegfx::B2DHomMatrix
const aOldTransform(
947 pCanvas
->getTransformation() );
948 basegfx::B2DHomMatrix aTransform
;
949 pCanvas
->setTransformation( aTransform
/* empty */ );
952 ::cppcanvas::Canvas
* pTmpCanvas
= pCanvas
.get();
953 comphelper::ScopeGuard
const resetOldTransformation(
954 [&aOldTransform
, &pTmpCanvas
]()
955 { return pTmpCanvas
->setTransformation( aOldTransform
); } );
957 aTransform
.scale( maBounds
.getWidth(),
958 maBounds
.getHeight() );
959 pRenderer
->setTransformation( aTransform
);
960 pRenderer
->setClip();
962 for( std::size_t pos
= maHyperlinkRegions
.size(); pos
--; )
965 HyperlinkIndexPair
const& rIndices
= maHyperlinkIndices
[pos
];
966 basegfx::B2DRectangle
const region(
967 pRenderer
->getSubsetArea( rIndices
.first
,
969 maHyperlinkRegions
[pos
].first
= region
;
974 // shift shape-relative hyperlink regions to
975 // slide-absolute position
977 HyperlinkRegions aTranslatedRegions
;
979 // increase capacity to same size as the container for
980 // shape-relative hyperlink regions to avoid reallocation
981 aTranslatedRegions
.reserve( maHyperlinkRegions
.size() );
982 const basegfx::B2DPoint
& rOffset(getBounds().getMinimum());
983 for( const auto& cp
: maHyperlinkRegions
)
985 basegfx::B2DRange
const& relRegion( cp
.first
);
986 aTranslatedRegions
.push_back(
989 relRegion
.getMinimum() + rOffset
,
990 relRegion
.getMaximum() + rOffset
),
994 return aTranslatedRegions
;
997 double DrawShape::getHyperlinkPriority() const
999 return getPriority();
1003 // AnimatableShape methods
1006 void DrawShape::enterAnimationMode()
1008 OSL_ENSURE( !maViewShapes
.empty(),
1009 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1011 if( mnIsAnimatedCount
== 0 )
1013 // notify all ViewShapes, by calling their enterAnimationMode method.
1014 // We're now entering animation mode
1015 for( const auto& rViewShape
: maViewShapes
)
1016 rViewShape
->enterAnimationMode();
1019 ++mnIsAnimatedCount
;
1022 void DrawShape::leaveAnimationMode()
1024 OSL_ENSURE( !maViewShapes
.empty(),
1025 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1027 --mnIsAnimatedCount
;
1029 if( mnIsAnimatedCount
== 0 )
1031 // notify all ViewShapes, by calling their leaveAnimationMode method.
1032 // we're now leaving animation mode
1033 for( const auto& rViewShape
: maViewShapes
)
1034 rViewShape
->leaveAnimationMode();
1039 // AttributableShape methods
1042 ShapeAttributeLayerSharedPtr
DrawShape::createAttributeLayer()
1044 // create new layer, with last as its new child
1045 mpAttributeLayer
.reset( new ShapeAttributeLayer( mpAttributeLayer
) );
1047 // Update the local state ids to reflect those of the new layer.
1050 return mpAttributeLayer
;
1053 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr
& rLayer
)
1055 if( !mpAttributeLayer
)
1056 return false; // no layers
1058 if( mpAttributeLayer
== rLayer
)
1060 // it's the toplevel layer
1061 mpAttributeLayer
= mpAttributeLayer
->getChildLayer();
1063 // force content redraw, all state variables have
1065 mbAttributeLayerRevoked
= true;
1071 // pass on to the layer, to try its children
1072 return mpAttributeLayer
->revokeChildLayer( rLayer
);
1076 ShapeAttributeLayerSharedPtr
DrawShape::getTopmostAttributeLayer() const
1078 return mpAttributeLayer
;
1081 void DrawShape::setVisibility( bool bVisible
)
1083 if( mbIsVisible
!= bVisible
)
1085 mbIsVisible
= bVisible
;
1086 mbForceUpdate
= true;
1090 const DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier() const
1095 DocTreeNodeSupplier
& DrawShape::getTreeNodeSupplier()
1100 DocTreeNode
DrawShape::getSubsetNode() const
1102 // forward to delegate
1103 return maSubsetting
.getSubsetNode();
1106 AttributableShapeSharedPtr
DrawShape::getSubset( const DocTreeNode
& rTreeNode
) const
1108 // forward to delegate
1109 return maSubsetting
.getSubsetShape( rTreeNode
);
1112 bool DrawShape::createSubset( AttributableShapeSharedPtr
& o_rSubset
,
1113 const DocTreeNode
& rTreeNode
)
1115 // subset shape already created for this DocTreeNode?
1116 AttributableShapeSharedPtr
pSubset( maSubsetting
.getSubsetShape( rTreeNode
) );
1118 // when true, this method has created a new subset
1120 bool bNewlyCreated( false );
1124 o_rSubset
= pSubset
;
1126 // reusing existing subset
1130 // not yet created, init entry
1131 o_rSubset
.reset( new DrawShape( *this,
1133 // TODO(Q3): That's a
1135 // that start and end
1136 // index will always
1137 // be less than 65535
1139 rTreeNode
.getStartIndex()/double(SAL_MAX_INT16
) ));
1141 bNewlyCreated
= true; // subset newly created
1144 // always register shape at DrawShapeSubsetting, to keep
1145 // refcount up-to-date
1146 maSubsetting
.addSubsetShape( o_rSubset
);
1148 // flush bounds cache
1149 maCurrentShapeUnitBounds
.reset();
1151 return bNewlyCreated
;
1154 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr
& rShape
)
1156 // flush bounds cache
1157 maCurrentShapeUnitBounds
.reset();
1159 // forward to delegate
1160 if( maSubsetting
.revokeSubsetShape( rShape
) )
1162 // force redraw, our content has possibly changed (as
1163 // one of the subsets now display within our shape
1165 mbForceUpdate
= true;
1167 // #i47428# TEMP FIX: synchronize visibility of subset
1170 // TODO(F3): Remove here, and implement
1171 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1172 // additional level of indirection: create a
1173 // persistent subset, containing all text/only the
1174 // background respectively. From _that_ object,
1175 // generate the temporary character subset shapes.
1176 const ShapeAttributeLayerSharedPtr
& rAttrLayer(
1177 rShape
->getTopmostAttributeLayer() );
1179 rAttrLayer
->isVisibilityValid() &&
1180 rAttrLayer
->getVisibility() != isVisible() )
1182 const bool bVisibility( rAttrLayer
->getVisibility() );
1184 // visibilities differ - adjust ours, then
1185 if( mpAttributeLayer
)
1186 mpAttributeLayer
->setVisibility( bVisibility
);
1188 mbIsVisible
= bVisibility
;
1199 sal_Int32
DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1201 return maSubsetting
.getNumberOfTreeNodes( eNodeType
);
1204 DocTreeNode
DrawShape::getTreeNode( sal_Int32 nNodeIndex
,
1205 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1207 if ( hasHyperlinks())
1209 prepareHyperlinkIndices();
1212 return maSubsetting
.getTreeNode( nNodeIndex
, eNodeType
);
1215 sal_Int32
DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode
& rParentNode
,
1216 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1218 return maSubsetting
.getNumberOfSubsetTreeNodes( rParentNode
, eNodeType
);
1221 DocTreeNode
DrawShape::getSubsetTreeNode( const DocTreeNode
& rParentNode
,
1222 sal_Int32 nNodeIndex
,
1223 DocTreeNode::NodeType eNodeType
) const // throw ShapeLoadFailedException
1225 return maSubsetting
.getSubsetTreeNode( rParentNode
, nNodeIndex
, eNodeType
);
1230 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */