android: Update app-specific/MIME type icons
[LibreOffice.git] / slideshow / source / engine / shapes / drawshape.cxx
blobcdda31da8a666400ec77ad77856f8193ac3c2fc8
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 <comphelper/diagnose_ex.hxx>
22 #include <sal/log.hxx>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <o3tl/safeint.hxx>
26 #include <utility>
27 #include <vcl/metaact.hxx>
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/graph.hxx>
31 #include <basegfx/numeric/ftools.hxx>
33 #include <com/sun/star/drawing/TextAnimationKind.hpp>
35 #include <comphelper/scopeguard.hxx>
37 #include <algorithm>
38 #include <iterator>
39 #include <functional>
41 #include "drawshapesubsetting.hxx"
42 #include "drawshape.hxx"
43 #include <eventqueue.hxx>
44 #include <wakeupevent.hxx>
45 #include <subsettableshapemanager.hxx>
46 #include "intrinsicanimationactivity.hxx"
47 #include <tools.hxx>
48 #include "gdimtftools.hxx"
49 #include "drawinglayeranimation.hxx"
51 using namespace ::com::sun::star;
54 namespace slideshow::internal
58 // Private methods
61 GDIMetaFileSharedPtr const & DrawShape::forceScrollTextMetaFile()
63 if ((mnCurrMtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != MTF_LOAD_SCROLL_TEXT_MTF)
65 // reload with added flags:
66 mnCurrMtfLoadFlags |= MTF_LOAD_SCROLL_TEXT_MTF;
67 mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY),
68 mxPage, mnCurrMtfLoadFlags,
69 mxComponentContext);
71 if (!mpCurrMtf)
72 mpCurrMtf = std::make_shared<GDIMetaFile>();
74 // TODO(F1): Currently, the scroll metafile will
75 // never contain any verbose text comments. Thus,
76 // can only display the full mtf content, no
77 // subsets.
78 maSubsetting.reset( mpCurrMtf );
80 // adapt maBounds. the requested scroll text metafile
81 // will typically have dimension different from the
82 // actual shape
83 ::basegfx::B2DRectangle aScrollRect, aPaintRect;
84 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect,
85 aPaintRect,
86 mpCurrMtf ),
87 "DrawShape::forceScrollTextMetaFile(): Could "
88 "not extract scroll anim rectangles from mtf" );
90 // take the larger one of the two rectangles (that
91 // should be the bound rect of the retrieved
92 // metafile)
93 if( aScrollRect.isInside( aPaintRect ) )
94 maBounds = aScrollRect;
95 else
96 maBounds = aPaintRect;
98 return mpCurrMtf;
101 void DrawShape::updateStateIds() const
103 // Update the states, we've just redrawn or created a new
104 // attribute layer.
105 if( mpAttributeLayer )
107 mnAttributeTransformationState = mpAttributeLayer->getTransformationState();
108 mnAttributeClipState = mpAttributeLayer->getClipState();
109 mnAttributeAlphaState = mpAttributeLayer->getAlphaState();
110 mnAttributePositionState = mpAttributeLayer->getPositionState();
111 mnAttributeContentState = mpAttributeLayer->getContentState();
112 mnAttributeVisibilityState = mpAttributeLayer->getVisibilityState();
116 ViewShape::RenderArgs DrawShape::getViewRenderArgs() const
118 return ViewShape::RenderArgs(
119 maBounds,
120 getUpdateArea(),
121 getBounds(),
122 getActualUnitShapeBounds(),
123 mpAttributeLayer,
124 maSubsetting.getActiveSubsets(),
125 mnPriority);
128 bool DrawShape::implRender( UpdateFlags nUpdateFlags ) const
130 SAL_INFO( "slideshow", "::presentation::internal::DrawShape::implRender()" );
131 SAL_INFO( "slideshow", "::presentation::internal::DrawShape: 0x" << std::hex << this );
133 // will perform the update now, clear update-enforcing
134 // flags
135 mbForceUpdate = false;
136 mbAttributeLayerRevoked = false;
138 ENSURE_OR_RETURN_FALSE( !maViewShapes.empty(),
139 "DrawShape::implRender(): render called on DrawShape without views" );
141 if( maBounds.isEmpty() )
143 // zero-sized shapes are effectively invisible,
144 // thus, we save us the rendering...
145 return true;
148 // redraw all view shapes, by calling their update() method
149 ViewShape::RenderArgs renderArgs( getViewRenderArgs() );
150 bool bVisible = isVisible();
151 if( o3tl::make_unsigned(::std::count_if( maViewShapes.begin(),
152 maViewShapes.end(),
153 [this, &bVisible, &renderArgs, &nUpdateFlags]
154 ( const ViewShapeSharedPtr& pShape )
155 { return pShape->update( this->mpCurrMtf,
156 renderArgs,
157 nUpdateFlags,
158 bVisible ); } ))
159 != maViewShapes.size() )
161 // at least one of the ViewShape::update() calls did return
162 // false - update failed on at least one ViewLayer
163 return false;
166 // successfully redrawn - update state IDs to detect next changes
167 updateStateIds();
169 return true;
172 UpdateFlags DrawShape::getUpdateFlags() const
174 // default: update nothing, unless ShapeAttributeStack
175 // tells us below, or if the attribute layer was revoked
176 UpdateFlags nUpdateFlags(UpdateFlags::NONE);
178 // possibly the whole shape content changed
179 if( mbAttributeLayerRevoked )
180 nUpdateFlags = UpdateFlags::Content;
183 // determine what has to be updated
186 // do we have an attribute layer?
187 if( mpAttributeLayer )
189 // Prevent nUpdateFlags to be modified when the shape is not
190 // visible, except when it just was hidden.
191 if (mpAttributeLayer->getVisibility()
192 || mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
194 if (mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
196 // Change of the visibility state is mapped to
197 // content change because when the visibility
198 // changes then usually a sprite is shown or hidden
199 // and the background under has to be painted once.
200 nUpdateFlags |= UpdateFlags::Content;
203 // TODO(P1): This can be done without conditional branching.
204 // See HAKMEM.
205 if( mpAttributeLayer->getPositionState() != mnAttributePositionState )
207 nUpdateFlags |= UpdateFlags::Position;
209 if( mpAttributeLayer->getAlphaState() != mnAttributeAlphaState )
211 nUpdateFlags |= UpdateFlags::Alpha;
213 if( mpAttributeLayer->getClipState() != mnAttributeClipState )
215 nUpdateFlags |= UpdateFlags::Clip;
217 if( mpAttributeLayer->getTransformationState() != mnAttributeTransformationState )
219 nUpdateFlags |= UpdateFlags::Transformation;
221 if( mpAttributeLayer->getContentState() != mnAttributeContentState )
223 nUpdateFlags |= UpdateFlags::Content;
228 return nUpdateFlags;
231 ::basegfx::B2DRectangle DrawShape::getActualUnitShapeBounds() const
233 ENSURE_OR_THROW( !maViewShapes.empty(),
234 "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
236 const VectorOfDocTreeNodes& rSubsets(
237 maSubsetting.getActiveSubsets() );
239 const ::basegfx::B2DRectangle aDefaultBounds( 0.0,0.0,1.0,1.0 );
241 // perform the cheapest check first
242 if( rSubsets.empty() )
244 // if subset contains the whole shape, no need to call
245 // the somewhat expensive bound calculation, since as
246 // long as the subset is empty, this branch will be
247 // taken.
248 return aDefaultBounds;
250 else
252 OSL_ENSURE( rSubsets.size() != 1 ||
253 !rSubsets.front().isEmpty(),
254 "DrawShape::getActualUnitShapeBounds() expects a "
255 "_non-empty_ subset vector for a subsetted shape!" );
257 // are the cached bounds still valid?
258 if( !maCurrentShapeUnitBounds )
260 // no, (re)generate them
261 // =====================
263 // setup cached values to defaults (might fail to
264 // retrieve true bounds below)
265 maCurrentShapeUnitBounds = aDefaultBounds;
267 // TODO(P2): the subset of the master shape (that from
268 // which the subsets are subtracted) changes
269 // relatively often (every time a subset shape is
270 // added or removed). Maybe we should exclude it here,
271 // always assuming full bounds?
273 ::cppcanvas::CanvasSharedPtr pDestinationCanvas(
274 maViewShapes.front()->getViewLayer()->getCanvas() );
276 // TODO(Q2): Although this _is_ currently
277 // view-agnostic, it might not stay like
278 // that. Maybe this method should again be moved
279 // to the ViewShape
280 ::cppcanvas::RendererSharedPtr pRenderer(
281 maViewShapes.front()->getRenderer(
282 pDestinationCanvas, mpCurrMtf, mpAttributeLayer ) );
284 // If we cannot not prefetch, be defensive and assume
285 // full shape size
286 if( pRenderer )
288 // temporarily, switch total transformation to identity
289 // (need the bounds in the [0,1]x[0,1] unit coordinate
290 // system.
291 ::basegfx::B2DHomMatrix aEmptyTransformation;
293 ::basegfx::B2DHomMatrix aOldTransform( pDestinationCanvas->getTransformation() );
294 pDestinationCanvas->setTransformation( aEmptyTransformation );
295 pRenderer->setTransformation( aEmptyTransformation );
297 // restore old transformation when leaving the scope
298 const ::comphelper::ScopeGuard aGuard(
299 [&pDestinationCanvas, &aOldTransform]()
300 { return pDestinationCanvas->setTransformation( aOldTransform ); } );
303 // retrieve bounds for subset of whole metafile
306 ::basegfx::B2DRange aTotalBounds;
308 // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
309 // is overloaded.
310 for( const auto& rDocTreeNode : rSubsets )
311 aTotalBounds.expand( pRenderer->getSubsetArea(
312 rDocTreeNode.getStartIndex(),
313 rDocTreeNode.getEndIndex() ) );
315 OSL_ENSURE( aTotalBounds.getMinX() >= -0.1 &&
316 aTotalBounds.getMinY() >= -0.1 &&
317 aTotalBounds.getMaxX() <= 1.1 &&
318 aTotalBounds.getMaxY() <= 1.1,
319 "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
321 // really make sure no shape appears larger than its
322 // original bounds (there _are_ some pathologic cases,
323 // especially when imported from PPT, that have
324 // e.g. obscenely large polygon bounds)
325 aTotalBounds.intersect(
326 ::basegfx::B2DRange( 0.0, 0.0,
327 1.0, 1.0 ));
329 maCurrentShapeUnitBounds = aTotalBounds;
333 return *maCurrentShapeUnitBounds;
337 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
338 const uno::Reference< drawing::XDrawPage >& xContainingPage,
339 double nPrio,
340 bool bForeignSource,
341 const SlideShowContext& rContext ) :
342 mxShape( xShape ),
343 mxPage( xContainingPage ),
344 maAnimationFrames(), // empty, we don't have no intrinsic animation
345 mnCurrFrame(0),
346 mpCurrMtf(),
347 mnCurrMtfLoadFlags( bForeignSource
348 ? MTF_LOAD_FOREIGN_SOURCE : MTF_LOAD_NONE ),
349 maCurrentShapeUnitBounds(),
350 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
351 maBounds( getAPIShapeBounds( xShape ) ),
352 mpAttributeLayer(),
353 mpIntrinsicAnimationActivity(),
354 mnAttributeTransformationState(0),
355 mnAttributeClipState(0),
356 mnAttributeAlphaState(0),
357 mnAttributePositionState(0),
358 mnAttributeContentState(0),
359 mnAttributeVisibilityState(0),
360 maViewShapes(),
361 mxComponentContext( rContext.mxComponentContext ),
362 maHyperlinkIndices(),
363 maHyperlinkRegions(),
364 maSubsetting(),
365 mnIsAnimatedCount(0),
366 mnAnimationLoopCount(0),
367 mbIsVisible( true ),
368 mbForceUpdate( false ),
369 mbAttributeLayerRevoked( false ),
370 mbDrawingLayerAnim( false ),
371 mbContainsPageField( false )
373 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
374 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
376 // check for drawing layer animations:
377 drawing::TextAnimationKind eKind = drawing::TextAnimationKind_NONE;
378 uno::Reference<beans::XPropertySet> xPropSet( mxShape,
379 uno::UNO_QUERY );
380 if( xPropSet.is() )
381 getPropertyValue( eKind, xPropSet,
382 "TextAnimationKind" );
383 mbDrawingLayerAnim = (eKind != drawing::TextAnimationKind_NONE);
385 // must NOT be called from within initializer list, uses
386 // state from mnCurrMtfLoadFlags!
387 mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY),
388 xContainingPage, mnCurrMtfLoadFlags,
389 mxComponentContext );
390 if (!mpCurrMtf)
391 mpCurrMtf = std::make_shared<GDIMetaFile>();
393 maSubsetting.reset( mpCurrMtf );
395 prepareHyperlinkIndices();
397 if(mbContainsPageField && !maBounds.isEmpty())
399 // tdf#150402 Use mbContainsPageField that gets set in prepareHyperlinkIndices
400 // which has to be run anyways, so this will cause no harm in execution speed.
401 // It lets us detect the potential error case that a PageField is contained in
402 // the Text of the Shape. That is a hint that maBounds contains the wrong Range
403 // and needs to be corrected. The correct size is in the PrefSize of the metafile.
404 // For more background information please refer to tdf#150402, Comment 16.
405 const double fWidthDiff(fabs(mpCurrMtf->GetPrefSize().Width() - maBounds.getWidth()));
406 const double fHeightDiff(fabs(mpCurrMtf->GetPrefSize().Height() - maBounds.getHeight()));
408 if(fWidthDiff > 1.0 || fHeightDiff > 1.0)
410 maBounds = basegfx::B2DRange(
411 maBounds.getMinX(), maBounds.getMinY(),
412 maBounds.getMinX() + mpCurrMtf->GetPrefSize().Width(),
413 maBounds.getMinY() + mpCurrMtf->GetPrefSize().Height());
418 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
419 uno::Reference< drawing::XDrawPage > xContainingPage,
420 double nPrio,
421 const Graphic& rGraphic,
422 const SlideShowContext& rContext ) :
423 mxShape( xShape ),
424 mxPage(std::move( xContainingPage )),
425 maAnimationFrames(),
426 mnCurrFrame(0),
427 mpCurrMtf(),
428 mnCurrMtfLoadFlags( MTF_LOAD_NONE ),
429 maCurrentShapeUnitBounds(),
430 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
431 maBounds( getAPIShapeBounds( xShape ) ),
432 mpAttributeLayer(),
433 mpIntrinsicAnimationActivity(),
434 mnAttributeTransformationState(0),
435 mnAttributeClipState(0),
436 mnAttributeAlphaState(0),
437 mnAttributePositionState(0),
438 mnAttributeContentState(0),
439 mnAttributeVisibilityState(0),
440 maViewShapes(),
441 mxComponentContext( rContext.mxComponentContext ),
442 maHyperlinkIndices(),
443 maHyperlinkRegions(),
444 maSubsetting(),
445 mnIsAnimatedCount(0),
446 mnAnimationLoopCount(0),
447 mbIsVisible( true ),
448 mbForceUpdate( false ),
449 mbAttributeLayerRevoked( false ),
450 mbDrawingLayerAnim( false ),
451 mbContainsPageField( false )
453 ENSURE_OR_THROW( rGraphic.IsAnimated(),
454 "DrawShape::DrawShape(): Graphic is no animation" );
456 getAnimationFromGraphic( maAnimationFrames,
457 mnAnimationLoopCount,
458 rGraphic );
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,
472 double nPrio ) :
473 mxShape( rSrc.mxShape ),
474 mxPage( rSrc.mxPage ),
475 maAnimationFrames(), // don't copy animations for subsets,
476 // only the current frame!
477 mnCurrFrame(0),
478 mpCurrMtf( rSrc.mpCurrMtf ),
479 mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ),
480 maCurrentShapeUnitBounds(),
481 mnPriority( nPrio ),
482 maBounds( rSrc.maBounds ),
483 mpAttributeLayer(),
484 mpIntrinsicAnimationActivity(),
485 mnAttributeTransformationState(0),
486 mnAttributeClipState(0),
487 mnAttributeAlphaState(0),
488 mnAttributePositionState(0),
489 mnAttributeContentState(0),
490 mnAttributeVisibilityState(0),
491 maViewShapes(),
492 mxComponentContext( rSrc.mxComponentContext ),
493 maHyperlinkIndices(),
494 maHyperlinkRegions(),
495 maSubsetting( rTreeNode, mpCurrMtf ),
496 mnIsAnimatedCount(0),
497 mnAnimationLoopCount(0),
498 mbIsVisible( rSrc.mbIsVisible ),
499 mbForceUpdate( false ),
500 mbAttributeLayerRevoked( false ),
501 mbDrawingLayerAnim( false ),
502 mbContainsPageField( 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#
514 // Public methods
517 DrawShapeSharedPtr DrawShape::create(
518 const uno::Reference< drawing::XShape >& xShape,
519 const uno::Reference< drawing::XDrawPage >& xContainingPage,
520 double nPrio,
521 bool bForeignSource,
522 const SlideShowContext& rContext )
524 DrawShapeSharedPtr pShape( new DrawShape(xShape,
525 xContainingPage,
526 nPrio,
527 bForeignSource,
528 rContext) );
530 if( pShape->hasIntrinsicAnimation() )
532 OSL_ASSERT( pShape->maAnimationFrames.empty() );
533 if( pShape->getNumberOfTreeNodes(
534 DocTreeNode::NodeType::LogicalParagraph) > 0 )
536 pShape->mpIntrinsicAnimationActivity =
537 createDrawingLayerAnimActivity(
538 rContext,
539 pShape);
543 if( pShape->hasHyperlinks() )
544 rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape );
546 return pShape;
549 DrawShapeSharedPtr DrawShape::create(
550 const uno::Reference< drawing::XShape >& xShape,
551 const uno::Reference< drawing::XDrawPage >& xContainingPage,
552 double nPrio,
553 const Graphic& rGraphic,
554 const SlideShowContext& rContext )
556 DrawShapeSharedPtr pShape( new DrawShape(xShape,
557 xContainingPage,
558 nPrio,
559 rGraphic,
560 rContext) );
562 if( pShape->hasIntrinsicAnimation() )
564 OSL_ASSERT( !pShape->maAnimationFrames.empty() );
566 std::vector<double> aTimeout;
567 std::transform(
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 std::make_shared<WakeupEvent>( rContext.mrEventQueue.getTimer(),
575 rContext.mrActivitiesQueue );
577 ActivitySharedPtr pActivity =
578 createIntrinsicAnimationActivity(
579 rContext,
580 pShape,
581 pWakeupEvent,
582 std::move(aTimeout),
583 pShape->mnAnimationLoopCount);
585 pWakeupEvent->setActivity( pActivity );
586 pShape->mpIntrinsicAnimationActivity = pActivity;
589 OSL_ENSURE( !pShape->hasHyperlinks(),
590 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
592 return pShape;
595 DrawShape::~DrawShape()
599 // dispose intrinsic animation activity, else, it will
600 // linger forever
601 ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() );
602 if( pActivity )
603 pActivity->dispose();
605 catch (uno::Exception const &)
607 DBG_UNHANDLED_EXCEPTION("slideshow");
611 uno::Reference< drawing::XShape > DrawShape::getXShape() const
613 return mxShape;
616 void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer,
617 bool bRedrawLayer )
619 // already added?
620 if( ::std::any_of( maViewShapes.begin(),
621 maViewShapes.end(),
622 [&rNewLayer]
623 ( const ViewShapeSharedPtr& pShape )
624 { return rNewLayer == pShape->getViewLayer(); } ) )
626 // yes, nothing to do
627 return;
630 ViewShapeSharedPtr pNewShape = std::make_shared<ViewShape>( rNewLayer );
632 maViewShapes.push_back( pNewShape );
634 // pass on animation state
635 if( mnIsAnimatedCount )
637 for( int i=0; i<mnIsAnimatedCount; ++i )
638 pNewShape->enterAnimationMode();
641 // render the Shape on the newly added ViewLayer
642 if( bRedrawLayer )
644 pNewShape->update( mpCurrMtf,
645 getViewRenderArgs(),
646 UpdateFlags::Force,
647 isVisible() );
651 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer )
653 const ViewShapeVector::iterator aEnd( maViewShapes.end() );
655 OSL_ENSURE( ::std::count_if(maViewShapes.begin(),
656 aEnd,
657 [&rLayer]
658 ( const ViewShapeSharedPtr& pShape )
659 { return rLayer == pShape->getViewLayer(); } ) < 2,
660 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
662 ViewShapeVector::iterator aIter;
664 if( (aIter=::std::remove_if( maViewShapes.begin(),
665 aEnd,
666 [&rLayer]
667 ( const ViewShapeSharedPtr& pShape )
668 { return rLayer == pShape->getViewLayer(); } ) ) == aEnd )
670 // view layer seemingly was not added, failed
671 return false;
674 // actually erase from container
675 maViewShapes.erase( aIter, aEnd );
677 return true;
680 void DrawShape::clearAllViewLayers()
682 maViewShapes.clear();
685 bool DrawShape::update() const
687 if( mbForceUpdate )
689 return render();
691 else
693 return implRender( getUpdateFlags() );
697 bool DrawShape::render() const
699 // force redraw. Have to also pass on the update flags,
700 // because e.g. content update (regeneration of the
701 // metafile renderer) is normally not performed. A simple
702 // UpdateFlags::Force would only paint the metafile in its
703 // old state.
704 return implRender( UpdateFlags::Force | getUpdateFlags() );
707 bool DrawShape::isContentChanged() const
709 return mbForceUpdate ||
710 getUpdateFlags() != UpdateFlags::NONE;
714 ::basegfx::B2DRectangle DrawShape::getBounds() const
716 // little optimization: for non-modified shapes, we don't
717 // create an ShapeAttributeStack, and therefore also don't
718 // have to check it.
719 return getShapePosSize( maBounds,
720 mpAttributeLayer );
723 ::basegfx::B2DRectangle DrawShape::getDomBounds() const
725 return maBounds;
728 ::basegfx::B2DRectangle DrawShape::getUpdateArea() const
730 ::basegfx::B2DRectangle aBounds;
732 // an already empty shape bound need no further
733 // treatment. In fact, any changes applied below would
734 // actually remove the special empty state, thus, don't
735 // change!
736 if( !maBounds.isEmpty() )
738 basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0);
740 if( !maViewShapes.empty() )
741 aUnitBounds = getActualUnitShapeBounds();
743 if( !aUnitBounds.isEmpty() )
745 if( mpAttributeLayer )
747 // calc actual shape area (in user coordinate
748 // space) from the transformation as given by the
749 // shape attribute layer
750 aBounds = getShapeUpdateArea( aUnitBounds,
751 getShapeTransformation( getBounds(),
752 mpAttributeLayer ),
753 mpAttributeLayer );
755 else
757 // no attribute layer, thus, the true shape bounds
758 // can be directly derived from the XShape bound
759 // attribute
760 aBounds = getShapeUpdateArea( aUnitBounds,
761 maBounds );
764 if( !maViewShapes.empty() )
766 // determine border needed for antialiasing the shape
767 ::basegfx::B2DSize aAABorder(0.0,0.0);
769 // for every view, get AA border and 'expand' aAABorder
770 // appropriately.
771 for( const auto& rViewShape : maViewShapes )
773 const ::basegfx::B2DSize rShapeBorder( rViewShape->getAntialiasingBorder() );
775 aAABorder.setWidth( ::std::max(
776 rShapeBorder.getWidth(),
777 aAABorder.getWidth() ) );
778 aAABorder.setHeight( ::std::max(
779 rShapeBorder.getHeight(),
780 aAABorder.getHeight() ) );
783 // add calculated AA border to aBounds
784 aBounds = ::basegfx::B2DRectangle( aBounds.getMinX() - aAABorder.getWidth(),
785 aBounds.getMinY() - aAABorder.getHeight(),
786 aBounds.getMaxX() + aAABorder.getWidth(),
787 aBounds.getMaxY() + aAABorder.getHeight() );
792 return aBounds;
795 bool DrawShape::isVisible() const
797 bool bIsVisible( mbIsVisible );
799 if( mpAttributeLayer )
801 // check whether visibility and alpha are not default
802 // (mpAttributeLayer->isVisibilityValid() returns true
803 // then): bVisible becomes true, if shape visibility
804 // is on and alpha is not 0.0 (fully transparent)
805 if( mpAttributeLayer->isVisibilityValid() )
806 bIsVisible = mpAttributeLayer->getVisibility();
808 // only touch bIsVisible, if the shape is still
809 // visible - if getVisibility already made us
810 // invisible, no alpha value will make us appear
811 // again.
812 if( bIsVisible && mpAttributeLayer->isAlphaValid() )
813 bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
816 return bIsVisible;
819 double DrawShape::getPriority() const
821 return mnPriority;
824 bool DrawShape::isBackgroundDetached() const
826 return mnIsAnimatedCount > 0;
829 bool DrawShape::hasIntrinsicAnimation() const
831 return (!maAnimationFrames.empty() || mbDrawingLayerAnim);
834 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame )
836 ENSURE_OR_RETURN_VOID( nCurrFrame < maAnimationFrames.size(),
837 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
839 if( mnCurrFrame != nCurrFrame )
841 mnCurrFrame = nCurrFrame;
842 mpCurrMtf = maAnimationFrames[ mnCurrFrame ].mpMtf;
843 mbForceUpdate = true;
847 // hyperlink support
848 void DrawShape::prepareHyperlinkIndices() const
850 if ( !maHyperlinkIndices.empty())
852 maHyperlinkIndices.clear();
853 maHyperlinkRegions.clear();
856 sal_Int32 nIndex = 0;
857 for ( MetaAction * pCurrAct = mpCurrMtf->FirstAction();
858 pCurrAct != nullptr; pCurrAct = mpCurrMtf->NextAction() )
860 if (pCurrAct->GetType() == MetaActionType::COMMENT) {
861 MetaCommentAction * pAct =
862 static_cast<MetaCommentAction *>(pCurrAct);
863 // skip comment if not a special XTEXT comment
864 if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
865 // e.g. date field doesn't have data!
866 // currently assuming that only url field, this is
867 // somehow fragile! xxx todo if possible
868 pAct->GetData() != nullptr &&
869 pAct->GetDataSize() > 0)
871 if (!maHyperlinkIndices.empty() &&
872 maHyperlinkIndices.back().second == -1) {
873 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
874 maHyperlinkIndices.pop_back();
875 maHyperlinkRegions.pop_back();
877 maHyperlinkIndices.emplace_back( nIndex + 1,
878 -1 /* to be filled below */ );
879 maHyperlinkRegions.emplace_back(
880 basegfx::B2DRectangle(),
881 OUString(
882 reinterpret_cast<sal_Unicode const*>(
883 pAct->GetData()),
884 pAct->GetDataSize() / sizeof(sal_Unicode) )
887 else if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
888 // pending end is expected:
889 !maHyperlinkIndices.empty() &&
890 maHyperlinkIndices.back().second == -1)
892 maHyperlinkIndices.back().second = nIndex;
894 else if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN;PageField"))
896 mbContainsPageField = true;
898 ++nIndex;
900 else
901 nIndex += getNextActionOffset(pCurrAct);
903 if (!maHyperlinkIndices.empty() &&
904 maHyperlinkIndices.back().second == -1) {
905 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
906 maHyperlinkIndices.pop_back();
907 maHyperlinkRegions.pop_back();
909 OSL_ASSERT( maHyperlinkIndices.size() == maHyperlinkRegions.size());
912 bool DrawShape::hasHyperlinks() const
914 return ! maHyperlinkRegions.empty();
917 HyperlinkArea::HyperlinkRegions DrawShape::getHyperlinkRegions() const
919 OSL_ASSERT( !maViewShapes.empty() );
921 if( !isVisible() )
922 return HyperlinkArea::HyperlinkRegions();
924 // late init, determine regions:
925 if( !maHyperlinkRegions.empty() &&
926 !maViewShapes.empty() &&
927 // region already inited?
928 maHyperlinkRegions.front().first.getWidth() == 0 &&
929 maHyperlinkRegions.front().first.getHeight() == 0 &&
930 maHyperlinkRegions.size() == maHyperlinkIndices.size() )
932 // TODO(Q2): Although this _is_ currently
933 // view-agnostic, it might not stay like that.
934 ViewShapeSharedPtr const& pViewShape = maViewShapes.front();
935 cppcanvas::CanvasSharedPtr const pCanvas(
936 pViewShape->getViewLayer()->getCanvas() );
938 // reuse Renderer of first view shape:
939 cppcanvas::RendererSharedPtr const pRenderer(
940 pViewShape->getRenderer(
941 pCanvas, mpCurrMtf, mpAttributeLayer ) );
943 OSL_ASSERT( pRenderer );
945 if (pRenderer)
947 basegfx::B2DHomMatrix const aOldTransform(
948 pCanvas->getTransformation() );
949 basegfx::B2DHomMatrix aTransform;
950 pCanvas->setTransformation( aTransform /* empty */ );
953 ::cppcanvas::Canvas* pTmpCanvas = pCanvas.get();
954 comphelper::ScopeGuard const resetOldTransformation(
955 [&aOldTransform, &pTmpCanvas]()
956 { return pTmpCanvas->setTransformation( aOldTransform ); } );
958 aTransform.scale( maBounds.getWidth(),
959 maBounds.getHeight() );
960 pRenderer->setTransformation( aTransform );
961 pRenderer->setClip();
963 for( std::size_t pos = maHyperlinkRegions.size(); pos--; )
965 // get region:
966 HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos];
967 basegfx::B2DRectangle const region(
968 pRenderer->getSubsetArea( rIndices.first,
969 rIndices.second ));
970 maHyperlinkRegions[pos].first = region;
975 // shift shape-relative hyperlink regions to
976 // slide-absolute position
978 HyperlinkRegions aTranslatedRegions;
980 // increase capacity to same size as the container for
981 // shape-relative hyperlink regions to avoid reallocation
982 aTranslatedRegions.reserve( maHyperlinkRegions.size() );
983 const basegfx::B2DPoint& rOffset(getBounds().getMinimum());
984 for( const auto& cp : maHyperlinkRegions )
986 basegfx::B2DRange const& relRegion( cp.first );
987 aTranslatedRegions.emplace_back(
988 basegfx::B2DRange(
989 relRegion.getMinimum() + rOffset,
990 relRegion.getMaximum() + rOffset),
991 cp.second );
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 = std::make_shared<ShapeAttributeLayer>( mpAttributeLayer );
1047 // Update the local state ids to reflect those of the new layer.
1048 updateStateIds();
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
1064 // possibly changed
1065 mbAttributeLayerRevoked = true;
1067 return true;
1069 else
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
1092 return *this;
1095 DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier()
1097 return *this;
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
1119 // DrawShape
1120 bool bNewlyCreated( false );
1122 if( pSubset )
1124 o_rSubset = pSubset;
1126 // reusing existing subset
1128 else
1130 // not yet created, init entry
1131 o_rSubset.reset( new DrawShape( *this,
1132 rTreeNode,
1133 // TODO(Q3): That's a
1134 // hack. We assume
1135 // that start and end
1136 // index will always
1137 // be less than 65535
1138 mnPriority +
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
1164 // again).
1165 mbForceUpdate = true;
1167 // #i47428# TEMP FIX: synchronize visibility of subset
1168 // with parent.
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() );
1178 if( rAttrLayer &&
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 );
1187 else
1188 mbIsVisible = bVisibility;
1191 // END TEMP FIX
1193 return true;
1196 return false;
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 );
1229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */