Use o3tl::convert in Math
[LibreOffice.git] / slideshow / source / engine / shapes / drawshape.cxx
blobf8e03ebff36f7a9922a0ca6bf7d2897115ea55f5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
35 #include <algorithm>
36 #include <iterator>
37 #include <functional>
39 #include "drawshapesubsetting.hxx"
40 #include "drawshape.hxx"
41 #include <eventqueue.hxx>
42 #include <wakeupevent.hxx>
43 #include <subsettableshapemanager.hxx>
44 #include "intrinsicanimationactivity.hxx"
45 #include <tools.hxx>
46 #include "gdimtftools.hxx"
47 #include "drawinglayeranimation.hxx"
49 using namespace ::com::sun::star;
52 namespace slideshow::internal
56 // Private methods
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,
67 mxComponentContext);
69 if (!mpCurrMtf)
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
75 // subsets.
76 maSubsetting.reset( mpCurrMtf );
78 // adapt maBounds. the requested scroll text metafile
79 // will typically have dimension different from the
80 // actual shape
81 ::basegfx::B2DRectangle aScrollRect, aPaintRect;
82 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect,
83 aPaintRect,
84 mpCurrMtf ),
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
90 // metafile)
91 if( aScrollRect.isInside( aPaintRect ) )
92 maBounds = aScrollRect;
93 else
94 maBounds = aPaintRect;
96 return mpCurrMtf;
99 void DrawShape::updateStateIds() const
101 // Update the states, we've just redrawn or created a new
102 // attribute layer.
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(
117 maBounds,
118 getUpdateArea(),
119 getBounds(),
120 getActualUnitShapeBounds(),
121 mpAttributeLayer,
122 maSubsetting.getActiveSubsets(),
123 mnPriority);
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
132 // flags
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...
143 return true;
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(),
150 maViewShapes.end(),
151 [this, &bVisible, &renderArgs, &nUpdateFlags]
152 ( const ViewShapeSharedPtr& pShape )
153 { return pShape->update( this->mpCurrMtf,
154 renderArgs,
155 nUpdateFlags,
156 bVisible ); } )
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
161 return false;
164 // successfully redrawn - update state IDs to detect next changes
165 updateStateIds();
167 return true;
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.
202 // See HAKMEM.
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;
226 return nUpdateFlags;
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
245 // taken.
246 return aDefaultBounds;
248 else
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
277 // to the ViewShape
278 ::cppcanvas::RendererSharedPtr pRenderer(
279 maViewShapes.front()->getRenderer(
280 pDestinationCanvas, mpCurrMtf, mpAttributeLayer ) );
282 // If we cannot not prefetch, be defensive and assume
283 // full shape size
284 if( pRenderer )
286 // temporarily, switch total transformation to identity
287 // (need the bounds in the [0,1]x[0,1] unit coordinate
288 // system.
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()
307 // is overloaded.
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,
325 1.0, 1.0 ));
327 maCurrentShapeUnitBounds = aTotalBounds;
331 return *maCurrentShapeUnitBounds;
335 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
336 const uno::Reference< drawing::XDrawPage >& xContainingPage,
337 double nPrio,
338 bool bForeignSource,
339 const SlideShowContext& rContext ) :
340 mxShape( xShape ),
341 mxPage( xContainingPage ),
342 maAnimationFrames(), // empty, we don't have no intrinsic animation
343 mnCurrFrame(0),
344 mpCurrMtf(),
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 ) ),
350 mpAttributeLayer(),
351 mpIntrinsicAnimationActivity(),
352 mnAttributeTransformationState(0),
353 mnAttributeClipState(0),
354 mnAttributeAlphaState(0),
355 mnAttributePositionState(0),
356 mnAttributeContentState(0),
357 mnAttributeVisibilityState(0),
358 maViewShapes(),
359 mxComponentContext( rContext.mxComponentContext ),
360 maHyperlinkIndices(),
361 maHyperlinkRegions(),
362 maSubsetting(),
363 mnIsAnimatedCount(0),
364 mnAnimationLoopCount(0),
365 mbIsVisible( true ),
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,
376 uno::UNO_QUERY );
377 if( xPropSet.is() )
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 );
387 if (!mpCurrMtf)
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,
397 double nPrio,
398 const Graphic& rGraphic,
399 const SlideShowContext& rContext ) :
400 mxShape( xShape ),
401 mxPage( xContainingPage ),
402 maAnimationFrames(),
403 mnCurrFrame(0),
404 mpCurrMtf(),
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 ) ),
409 mpAttributeLayer(),
410 mpIntrinsicAnimationActivity(),
411 mnAttributeTransformationState(0),
412 mnAttributeClipState(0),
413 mnAttributeAlphaState(0),
414 mnAttributePositionState(0),
415 mnAttributeContentState(0),
416 mnAttributeVisibilityState(0),
417 maViewShapes(),
418 mxComponentContext( rContext.mxComponentContext ),
419 maHyperlinkIndices(),
420 maHyperlinkRegions(),
421 maSubsetting(),
422 mnIsAnimatedCount(0),
423 mnAnimationLoopCount(0),
424 mbIsVisible( true ),
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,
434 rGraphic );
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,
448 double nPrio ) :
449 mxShape( rSrc.mxShape ),
450 mxPage( rSrc.mxPage ),
451 maAnimationFrames(), // don't copy animations for subsets,
452 // only the current frame!
453 mnCurrFrame(0),
454 mpCurrMtf( rSrc.mpCurrMtf ),
455 mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ),
456 maCurrentShapeUnitBounds(),
457 mnPriority( nPrio ),
458 maBounds( rSrc.maBounds ),
459 mpAttributeLayer(),
460 mpIntrinsicAnimationActivity(),
461 mnAttributeTransformationState(0),
462 mnAttributeClipState(0),
463 mnAttributeAlphaState(0),
464 mnAttributePositionState(0),
465 mnAttributeContentState(0),
466 mnAttributeVisibilityState(0),
467 maViewShapes(),
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#
489 // Public methods
492 DrawShapeSharedPtr DrawShape::create(
493 const uno::Reference< drawing::XShape >& xShape,
494 const uno::Reference< drawing::XDrawPage >& xContainingPage,
495 double nPrio,
496 bool bForeignSource,
497 const SlideShowContext& rContext )
499 DrawShapeSharedPtr pShape( new DrawShape(xShape,
500 xContainingPage,
501 nPrio,
502 bForeignSource,
503 rContext) );
505 if( pShape->hasIntrinsicAnimation() )
507 OSL_ASSERT( pShape->maAnimationFrames.empty() );
508 if( pShape->getNumberOfTreeNodes(
509 DocTreeNode::NodeType::LogicalParagraph) > 0 )
511 pShape->mpIntrinsicAnimationActivity =
512 createDrawingLayerAnimActivity(
513 rContext,
514 pShape);
518 if( pShape->hasHyperlinks() )
519 rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape );
521 return pShape;
524 DrawShapeSharedPtr DrawShape::create(
525 const uno::Reference< drawing::XShape >& xShape,
526 const uno::Reference< drawing::XDrawPage >& xContainingPage,
527 double nPrio,
528 const Graphic& rGraphic,
529 const SlideShowContext& rContext )
531 DrawShapeSharedPtr pShape( new DrawShape(xShape,
532 xContainingPage,
533 nPrio,
534 rGraphic,
535 rContext) );
537 if( pShape->hasIntrinsicAnimation() )
539 OSL_ASSERT( !pShape->maAnimationFrames.empty() );
541 std::vector<double> aTimeout;
542 std::transform(
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(
554 rContext,
555 pShape,
556 pWakeupEvent,
557 std::move(aTimeout),
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!" );
567 return pShape;
570 DrawShape::~DrawShape()
574 // dispose intrinsic animation activity, else, it will
575 // linger forever
576 ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() );
577 if( pActivity )
578 pActivity->dispose();
580 catch (uno::Exception const &)
582 DBG_UNHANDLED_EXCEPTION("slideshow");
586 uno::Reference< drawing::XShape > DrawShape::getXShape() const
588 return mxShape;
591 void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer,
592 bool bRedrawLayer )
594 // already added?
595 if( ::std::any_of( maViewShapes.begin(),
596 maViewShapes.end(),
597 [&rNewLayer]
598 ( const ViewShapeSharedPtr& pShape )
599 { return rNewLayer == pShape->getViewLayer(); } ) )
601 // yes, nothing to do
602 return;
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
617 if( bRedrawLayer )
619 pNewShape->update( mpCurrMtf,
620 getViewRenderArgs(),
621 UpdateFlags::Force,
622 isVisible() );
626 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer )
628 const ViewShapeVector::iterator aEnd( maViewShapes.end() );
630 OSL_ENSURE( ::std::count_if(maViewShapes.begin(),
631 aEnd,
632 [&rLayer]
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(),
640 aEnd,
641 [&rLayer]
642 ( const ViewShapeSharedPtr& pShape )
643 { return rLayer == pShape->getViewLayer(); } ) ) == aEnd )
645 // view layer seemingly was not added, failed
646 return false;
649 // actually erase from container
650 maViewShapes.erase( aIter, aEnd );
652 return true;
655 void DrawShape::clearAllViewLayers()
657 maViewShapes.clear();
660 bool DrawShape::update() const
662 if( mbForceUpdate )
664 return render();
666 else
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
678 // old state.
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
693 // have to check it.
694 return getShapePosSize( maBounds,
695 mpAttributeLayer );
698 ::basegfx::B2DRectangle DrawShape::getDomBounds() const
700 return maBounds;
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
710 // change!
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(),
727 mpAttributeLayer ),
728 mpAttributeLayer );
730 else
732 // no attribute layer, thus, the true shape bounds
733 // can be directly derived from the XShape bound
734 // attribute
735 aBounds = getShapeUpdateArea( aUnitBounds,
736 maBounds );
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
745 // appropriately.
746 for( const auto& rViewShape : maViewShapes )
748 const ::basegfx::B2DSize rShapeBorder( rViewShape->getAntialiasingBorder() );
750 aAABorder.setX( ::std::max(
751 rShapeBorder.getX(),
752 aAABorder.getX() ) );
753 aAABorder.setY( ::std::max(
754 rShapeBorder.getY(),
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() );
767 return aBounds;
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
786 // again.
787 if( bIsVisible && mpAttributeLayer->isAlphaValid() )
788 bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
791 return bIsVisible;
794 double DrawShape::getPriority() const
796 return mnPriority;
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;
822 // hyperlink support
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(),
856 OUString(
857 reinterpret_cast<sal_Unicode const*>(
858 pAct->GetData()),
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;
869 ++nIndex;
871 else
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() );
892 if( !isVisible() )
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 );
916 if (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--; )
936 // get region:
937 HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos];
938 basegfx::B2DRectangle const region(
939 pRenderer->getSubsetArea( rIndices.first,
940 rIndices.second ));
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(
959 basegfx::B2DRange(
960 relRegion.getMinimum() + rOffset,
961 relRegion.getMaximum() + rOffset),
962 cp.second );
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();
990 ++mnIsAnimatedCount;
993 void DrawShape::leaveAnimationMode()
995 OSL_ENSURE( !maViewShapes.empty(),
996 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
998 --mnIsAnimatedCount;
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.
1019 updateStateIds();
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
1035 // possibly changed
1036 mbAttributeLayerRevoked = true;
1038 return true;
1040 else
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
1063 return *this;
1066 DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier()
1068 return *this;
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
1090 // DrawShape
1091 bool bNewlyCreated( false );
1093 if( pSubset )
1095 o_rSubset = pSubset;
1097 // reusing existing subset
1099 else
1101 // not yet created, init entry
1102 o_rSubset.reset( new DrawShape( *this,
1103 rTreeNode,
1104 // TODO(Q3): That's a
1105 // hack. We assume
1106 // that start and end
1107 // index will always
1108 // be less than 65535
1109 mnPriority +
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
1135 // again).
1136 mbForceUpdate = true;
1138 // #i47428# TEMP FIX: synchronize visibility of subset
1139 // with parent.
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() );
1149 if( rAttrLayer &&
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 );
1158 else
1159 mbIsVisible = bVisibility;
1162 // END TEMP FIX
1164 return true;
1167 return false;
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: */