tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / slideshow / source / engine / shapes / drawshape.cxx
blob2f882c017da4bac04bbd7802ea93c5105a258467
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 mpGraphicLoader(),
347 mpCurrMtf(),
348 mnCurrMtfLoadFlags( bForeignSource
349 ? MTF_LOAD_FOREIGN_SOURCE : MTF_LOAD_NONE ),
350 maCurrentShapeUnitBounds(),
351 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
352 maBounds( getAPIShapeBounds( xShape ) ),
353 mpAttributeLayer(),
354 mpIntrinsicAnimationActivity(),
355 mnAttributeTransformationState(0),
356 mnAttributeClipState(0),
357 mnAttributeAlphaState(0),
358 mnAttributePositionState(0),
359 mnAttributeContentState(0),
360 mnAttributeVisibilityState(0),
361 maViewShapes(),
362 mxComponentContext( rContext.mxComponentContext ),
363 maHyperlinkIndices(),
364 maHyperlinkRegions(),
365 maSubsetting(),
366 mnIsAnimatedCount(0),
367 mnAnimationLoopCount(0),
368 mbIsVisible( true ),
369 mbForceUpdate( false ),
370 mbAttributeLayerRevoked( false ),
371 mbDrawingLayerAnim( false ),
372 mbContainsPageField( false )
374 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
375 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
377 // check for drawing layer animations:
378 drawing::TextAnimationKind eKind = drawing::TextAnimationKind_NONE;
379 uno::Reference<beans::XPropertySet> xPropSet( mxShape,
380 uno::UNO_QUERY );
381 if( xPropSet.is() )
382 getPropertyValue( eKind, xPropSet,
383 u"TextAnimationKind"_ustr );
384 mbDrawingLayerAnim = (eKind != drawing::TextAnimationKind_NONE);
386 // must NOT be called from within initializer list, uses
387 // state from mnCurrMtfLoadFlags!
388 mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY),
389 xContainingPage, mnCurrMtfLoadFlags,
390 mxComponentContext );
391 if (!mpCurrMtf)
392 mpCurrMtf = std::make_shared<GDIMetaFile>();
394 maSubsetting.reset( mpCurrMtf );
396 prepareHyperlinkIndices();
398 if(mbContainsPageField && !maBounds.isEmpty())
400 // tdf#150402 Use mbContainsPageField that gets set in prepareHyperlinkIndices
401 // which has to be run anyways, so this will cause no harm in execution speed.
402 // It lets us detect the potential error case that a PageField is contained in
403 // the Text of the Shape. That is a hint that maBounds contains the wrong Range
404 // and needs to be corrected. The correct size is in the PrefSize of the metafile.
405 // For more background information please refer to tdf#150402, Comment 16.
406 const double fWidthDiff(fabs(mpCurrMtf->GetPrefSize().Width() - maBounds.getWidth()));
407 const double fHeightDiff(fabs(mpCurrMtf->GetPrefSize().Height() - maBounds.getHeight()));
409 if(fWidthDiff > 1.0 || fHeightDiff > 1.0)
411 maBounds = basegfx::B2DRange(
412 maBounds.getMinX(), maBounds.getMinY(),
413 maBounds.getMinX() + mpCurrMtf->GetPrefSize().Width(),
414 maBounds.getMinY() + mpCurrMtf->GetPrefSize().Height());
419 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
420 uno::Reference< drawing::XDrawPage > xContainingPage,
421 double nPrio,
422 std::shared_ptr<Graphic> pGraphic,
423 const SlideShowContext& rContext ) :
424 mxShape( xShape ),
425 mxPage(std::move( xContainingPage )),
426 maAnimationFrames(),
427 mnCurrFrame(0),
428 mpGraphicLoader(),
429 mpCurrMtf(),
430 mnCurrMtfLoadFlags( MTF_LOAD_NONE ),
431 maCurrentShapeUnitBounds(),
432 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
433 maBounds( getAPIShapeBounds( xShape ) ),
434 mpAttributeLayer(),
435 mpIntrinsicAnimationActivity(),
436 mnAttributeTransformationState(0),
437 mnAttributeClipState(0),
438 mnAttributeAlphaState(0),
439 mnAttributePositionState(0),
440 mnAttributeContentState(0),
441 mnAttributeVisibilityState(0),
442 maViewShapes(),
443 mxComponentContext( rContext.mxComponentContext ),
444 maHyperlinkIndices(),
445 maHyperlinkRegions(),
446 maSubsetting(),
447 mnIsAnimatedCount(0),
448 mnAnimationLoopCount(0),
449 mbIsVisible( true ),
450 mbForceUpdate( false ),
451 mbAttributeLayerRevoked( false ),
452 mbDrawingLayerAnim( false ),
453 mbContainsPageField( false )
455 ENSURE_OR_THROW( pGraphic->IsAnimated(),
456 "DrawShape::DrawShape(): Graphic is no animation" );
458 ::Animation aAnimation(pGraphic->GetAnimation());
459 const Size aAnimSize(aAnimation.GetDisplaySizePixel());
460 tools::Long nBitmapPixels = aAnimSize.getWidth() * aAnimSize.getHeight();
462 tools::Long nFramesToLoad = aAnimation.Count();
464 // if the Animation is bigger then 5 million pixels, we do not load the
465 // whole animation now.
466 if (nBitmapPixels * aAnimation.Count() > 5000000)
468 nFramesToLoad = 5000000 / nBitmapPixels;
469 if (nFramesToLoad < 10)
470 nFramesToLoad = 10;
472 mpGraphicLoader = ::std::make_unique<DelayedGraphicLoader>(pGraphic);
473 getSomeAnimationFramesFromGraphic(nFramesToLoad);
475 ENSURE_OR_THROW( !maAnimationFrames.empty() &&
476 maAnimationFrames.front().mpMtf,
477 "DrawShape::DrawShape(): " );
478 mpCurrMtf = maAnimationFrames.front().mpMtf;
480 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
481 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
482 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
485 DrawShape::DrawShape( const DrawShape& rSrc,
486 const DocTreeNode& rTreeNode,
487 double nPrio ) :
488 mxShape( rSrc.mxShape ),
489 mxPage( rSrc.mxPage ),
490 maAnimationFrames(), // don't copy animations for subsets,
491 // only the current frame!
492 mnCurrFrame(0),
493 mpGraphicLoader(),
494 mpCurrMtf( rSrc.mpCurrMtf ),
495 mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ),
496 maCurrentShapeUnitBounds(),
497 mnPriority( nPrio ),
498 maBounds( rSrc.maBounds ),
499 mpAttributeLayer(),
500 mpIntrinsicAnimationActivity(),
501 mnAttributeTransformationState(0),
502 mnAttributeClipState(0),
503 mnAttributeAlphaState(0),
504 mnAttributePositionState(0),
505 mnAttributeContentState(0),
506 mnAttributeVisibilityState(0),
507 maViewShapes(),
508 mxComponentContext( rSrc.mxComponentContext ),
509 maHyperlinkIndices(),
510 maHyperlinkRegions(),
511 maSubsetting( rTreeNode, mpCurrMtf ),
512 mnIsAnimatedCount(0),
513 mnAnimationLoopCount(0),
514 mbIsVisible( rSrc.mbIsVisible ),
515 mbForceUpdate( false ),
516 mbAttributeLayerRevoked( false ),
517 mbDrawingLayerAnim( false ),
518 mbContainsPageField( false )
520 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
521 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
523 // xxx todo: currently not implemented for subsetted shapes;
524 // would mean modifying set of hyperlink regions when
525 // subsetting text portions. N.B.: there's already an
526 // issue for this #i72828#
530 // Public methods
533 DrawShapeSharedPtr DrawShape::create(
534 const uno::Reference< drawing::XShape >& xShape,
535 const uno::Reference< drawing::XDrawPage >& xContainingPage,
536 double nPrio,
537 bool bForeignSource,
538 const SlideShowContext& rContext )
540 DrawShapeSharedPtr pShape( new DrawShape(xShape,
541 xContainingPage,
542 nPrio,
543 bForeignSource,
544 rContext) );
546 if( pShape->hasIntrinsicAnimation() )
548 OSL_ASSERT( pShape->maAnimationFrames.empty() );
549 if( pShape->getNumberOfTreeNodes(
550 DocTreeNode::NodeType::LogicalParagraph) > 0 )
552 pShape->mpIntrinsicAnimationActivity =
553 createDrawingLayerAnimActivity(
554 rContext,
555 pShape);
559 if( pShape->hasHyperlinks() )
560 rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape );
562 return pShape;
565 DrawShapeSharedPtr DrawShape::create(
566 const uno::Reference< drawing::XShape >& xShape,
567 const uno::Reference< drawing::XDrawPage >& xContainingPage,
568 double nPrio,
569 std::shared_ptr<Graphic> pGraphic,
570 const SlideShowContext& rContext )
572 DrawShapeSharedPtr pShape( new DrawShape(xShape,
573 xContainingPage,
574 nPrio,
575 std::move(pGraphic),
576 rContext) );
578 if( pShape->hasIntrinsicAnimation() )
580 OSL_ASSERT( !pShape->maAnimationFrames.empty() );
582 std::vector<double> aTimeout;
583 std::transform(
584 pShape->maAnimationFrames.begin(),
585 pShape->maAnimationFrames.end(),
586 std::back_insert_iterator< std::vector<double> >( aTimeout ),
587 std::mem_fn(&MtfAnimationFrame::getDuration) );
589 WakeupEventSharedPtr pWakeupEvent =
590 std::make_shared<WakeupEvent>( rContext.mrEventQueue.getTimer(),
591 rContext.mrActivitiesQueue );
593 ActivitySharedPtr pActivity =
594 createIntrinsicAnimationActivity(
595 rContext,
596 pShape,
597 pWakeupEvent,
598 std::move(aTimeout),
599 pShape->mnAnimationLoopCount);
601 pWakeupEvent->setActivity( pActivity );
602 pShape->mpIntrinsicAnimationActivity = pActivity;
605 OSL_ENSURE( !pShape->hasHyperlinks(),
606 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
608 return pShape;
611 DrawShape::~DrawShape()
615 // dispose intrinsic animation activity, else, it will
616 // linger forever
617 ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() );
618 if( pActivity )
619 pActivity->dispose();
621 catch (uno::Exception const &)
623 DBG_UNHANDLED_EXCEPTION("slideshow");
627 uno::Reference< drawing::XShape > DrawShape::getXShape() const
629 return mxShape;
632 void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer,
633 bool bRedrawLayer )
635 // already added?
636 if( ::std::any_of( maViewShapes.begin(),
637 maViewShapes.end(),
638 [&rNewLayer]
639 ( const ViewShapeSharedPtr& pShape )
640 { return rNewLayer == pShape->getViewLayer(); } ) )
642 // yes, nothing to do
643 return;
646 ViewShapeSharedPtr pNewShape = std::make_shared<ViewShape>( rNewLayer );
648 maViewShapes.push_back( pNewShape );
650 // pass on animation state
651 if( mnIsAnimatedCount )
653 for( int i=0; i<mnIsAnimatedCount; ++i )
654 pNewShape->enterAnimationMode();
657 // render the Shape on the newly added ViewLayer
658 if( bRedrawLayer )
660 pNewShape->update( mpCurrMtf,
661 getViewRenderArgs(),
662 UpdateFlags::Force,
663 isVisible() );
667 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer )
669 const ViewShapeVector::iterator aEnd( maViewShapes.end() );
671 OSL_ENSURE( ::std::count_if(maViewShapes.begin(),
672 aEnd,
673 [&rLayer]
674 ( const ViewShapeSharedPtr& pShape )
675 { return rLayer == pShape->getViewLayer(); } ) < 2,
676 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
679 // TODO : needed for the moment since ANDROID doesn't know size_t return from std::erase_if
680 #if defined ANDROID
681 ViewShapeVector::iterator aIter;
683 if( (aIter=::std::remove_if( maViewShapes.begin(),
684 aEnd,
685 [&rLayer]
686 ( const ViewShapeSharedPtr& pShape )
687 { return rLayer == pShape->getViewLayer(); } ) ) == aEnd )
689 // view layer seemingly was not added, failed
690 return false;
693 // actually erase from container
694 maViewShapes.erase( aIter, aEnd );
696 return true;
697 #else
698 size_t nb = std::erase_if(maViewShapes,
699 [&rLayer]
700 ( const ViewShapeSharedPtr& pShape )
701 { return rLayer == pShape->getViewLayer(); } );
702 // if nb == 0, it means view shape seemingly was not added, failed
703 return (nb != 0);
704 #endif
707 void DrawShape::clearAllViewLayers()
709 maViewShapes.clear();
712 bool DrawShape::update() const
714 if( mbForceUpdate )
716 return render();
718 else
720 return implRender( getUpdateFlags() );
724 bool DrawShape::render() const
726 // force redraw. Have to also pass on the update flags,
727 // because e.g. content update (regeneration of the
728 // metafile renderer) is normally not performed. A simple
729 // UpdateFlags::Force would only paint the metafile in its
730 // old state.
731 return implRender( UpdateFlags::Force | getUpdateFlags() );
734 bool DrawShape::isContentChanged() const
736 return mbForceUpdate ||
737 getUpdateFlags() != UpdateFlags::NONE;
741 ::basegfx::B2DRectangle DrawShape::getBounds() const
743 // little optimization: for non-modified shapes, we don't
744 // create an ShapeAttributeStack, and therefore also don't
745 // have to check it.
746 return getShapePosSize( maBounds,
747 mpAttributeLayer );
750 ::basegfx::B2DRectangle DrawShape::getDomBounds() const
752 return maBounds;
755 ::basegfx::B2DRectangle DrawShape::getUpdateArea() const
757 ::basegfx::B2DRectangle aBounds;
759 // an already empty shape bound need no further
760 // treatment. In fact, any changes applied below would
761 // actually remove the special empty state, thus, don't
762 // change!
763 if( !maBounds.isEmpty() )
765 basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0);
767 if( !maViewShapes.empty() )
768 aUnitBounds = getActualUnitShapeBounds();
770 if( !aUnitBounds.isEmpty() )
772 if( mpAttributeLayer )
774 // calc actual shape area (in user coordinate
775 // space) from the transformation as given by the
776 // shape attribute layer
777 aBounds = getShapeUpdateArea( aUnitBounds,
778 getShapeTransformation( getBounds(),
779 mpAttributeLayer ),
780 mpAttributeLayer );
782 else
784 // no attribute layer, thus, the true shape bounds
785 // can be directly derived from the XShape bound
786 // attribute
787 aBounds = getShapeUpdateArea( aUnitBounds,
788 maBounds );
791 if( !maViewShapes.empty() )
793 // determine border needed for antialiasing the shape
794 ::basegfx::B2DSize aAABorder(0.0,0.0);
796 // for every view, get AA border and 'expand' aAABorder
797 // appropriately.
798 for( const auto& rViewShape : maViewShapes )
800 const ::basegfx::B2DSize rShapeBorder( rViewShape->getAntialiasingBorder() );
802 aAABorder.setWidth( ::std::max(
803 rShapeBorder.getWidth(),
804 aAABorder.getWidth() ) );
805 aAABorder.setHeight( ::std::max(
806 rShapeBorder.getHeight(),
807 aAABorder.getHeight() ) );
810 // add calculated AA border to aBounds
811 aBounds = ::basegfx::B2DRectangle( aBounds.getMinX() - aAABorder.getWidth(),
812 aBounds.getMinY() - aAABorder.getHeight(),
813 aBounds.getMaxX() + aAABorder.getWidth(),
814 aBounds.getMaxY() + aAABorder.getHeight() );
819 return aBounds;
822 bool DrawShape::isVisible() const
824 bool bIsVisible( mbIsVisible );
826 if( mpAttributeLayer )
828 // check whether visibility and alpha are not default
829 // (mpAttributeLayer->isVisibilityValid() returns true
830 // then): bVisible becomes true, if shape visibility
831 // is on and alpha is not 0.0 (fully transparent)
832 if( mpAttributeLayer->isVisibilityValid() )
833 bIsVisible = mpAttributeLayer->getVisibility();
835 // only touch bIsVisible, if the shape is still
836 // visible - if getVisibility already made us
837 // invisible, no alpha value will make us appear
838 // again.
839 if( bIsVisible && mpAttributeLayer->isAlphaValid() )
840 bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
843 return bIsVisible;
846 double DrawShape::getPriority() const
848 return mnPriority;
851 bool DrawShape::isBackgroundDetached() const
853 return mnIsAnimatedCount > 0;
856 bool DrawShape::hasIntrinsicAnimation() const
858 return (!maAnimationFrames.empty() || mbDrawingLayerAnim);
861 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame )
863 ENSURE_OR_RETURN_VOID( nCurrFrame < maAnimationFrames.size(),
864 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
866 // Load 1 more frame if needed. (make sure the current frame is loaded)
867 if (mpGraphicLoader)
868 getSomeAnimationFramesFromGraphic(1, nCurrFrame);
870 if( mnCurrFrame != nCurrFrame )
872 mnCurrFrame = nCurrFrame;
873 mpCurrMtf = maAnimationFrames[ mnCurrFrame ].mpMtf;
874 mbForceUpdate = true;
878 // hyperlink support
879 void DrawShape::prepareHyperlinkIndices() const
881 if ( !maHyperlinkIndices.empty())
883 maHyperlinkIndices.clear();
884 maHyperlinkRegions.clear();
887 sal_Int32 nIndex = 0;
888 for ( MetaAction * pCurrAct = mpCurrMtf->FirstAction();
889 pCurrAct != nullptr; pCurrAct = mpCurrMtf->NextAction() )
891 if (pCurrAct->GetType() == MetaActionType::COMMENT) {
892 MetaCommentAction * pAct =
893 static_cast<MetaCommentAction *>(pCurrAct);
894 // skip comment if not a special XTEXT comment
895 if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
896 // e.g. date field doesn't have data!
897 // currently assuming that only url field, this is
898 // somehow fragile! xxx todo if possible
899 pAct->GetData() != nullptr &&
900 pAct->GetDataSize() > 0)
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 maHyperlinkIndices.emplace_back( nIndex + 1,
909 -1 /* to be filled below */ );
910 maHyperlinkRegions.emplace_back(
911 basegfx::B2DRectangle(),
912 OUString(
913 reinterpret_cast<sal_Unicode const*>(
914 pAct->GetData()),
915 pAct->GetDataSize() / sizeof(sal_Unicode) )
918 else if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
919 // pending end is expected:
920 !maHyperlinkIndices.empty() &&
921 maHyperlinkIndices.back().second == -1)
923 maHyperlinkIndices.back().second = nIndex;
925 else if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN;PageField"))
927 mbContainsPageField = true;
929 ++nIndex;
931 else
932 nIndex += getNextActionOffset(pCurrAct);
934 if (!maHyperlinkIndices.empty() &&
935 maHyperlinkIndices.back().second == -1) {
936 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
937 maHyperlinkIndices.pop_back();
938 maHyperlinkRegions.pop_back();
940 OSL_ASSERT( maHyperlinkIndices.size() == maHyperlinkRegions.size());
943 bool DrawShape::hasHyperlinks() const
945 return ! maHyperlinkRegions.empty();
948 HyperlinkArea::HyperlinkRegions DrawShape::getHyperlinkRegions() const
950 OSL_ASSERT( !maViewShapes.empty() );
952 if( !isVisible() )
953 return HyperlinkArea::HyperlinkRegions();
955 // late init, determine regions:
956 if( !maHyperlinkRegions.empty() &&
957 !maViewShapes.empty() &&
958 // region already inited?
959 maHyperlinkRegions.front().first.getWidth() == 0 &&
960 maHyperlinkRegions.front().first.getHeight() == 0 &&
961 maHyperlinkRegions.size() == maHyperlinkIndices.size() )
963 // TODO(Q2): Although this _is_ currently
964 // view-agnostic, it might not stay like that.
965 ViewShapeSharedPtr const& pViewShape = maViewShapes.front();
966 cppcanvas::CanvasSharedPtr const pCanvas(
967 pViewShape->getViewLayer()->getCanvas() );
969 // reuse Renderer of first view shape:
970 cppcanvas::RendererSharedPtr const pRenderer(
971 pViewShape->getRenderer(
972 pCanvas, mpCurrMtf, mpAttributeLayer ) );
974 OSL_ASSERT( pRenderer );
976 if (pRenderer)
978 basegfx::B2DHomMatrix const aOldTransform(
979 pCanvas->getTransformation() );
980 basegfx::B2DHomMatrix aTransform;
981 pCanvas->setTransformation( aTransform /* empty */ );
984 ::cppcanvas::Canvas* pTmpCanvas = pCanvas.get();
985 comphelper::ScopeGuard const resetOldTransformation(
986 [&aOldTransform, &pTmpCanvas]()
987 { return pTmpCanvas->setTransformation( aOldTransform ); } );
989 aTransform.scale( maBounds.getWidth(),
990 maBounds.getHeight() );
991 pRenderer->setTransformation( aTransform );
992 pRenderer->setClip();
994 for( std::size_t pos = maHyperlinkRegions.size(); pos--; )
996 // get region:
997 HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos];
998 basegfx::B2DRectangle const region(
999 pRenderer->getSubsetArea( rIndices.first,
1000 rIndices.second ));
1001 maHyperlinkRegions[pos].first = region;
1006 // shift shape-relative hyperlink regions to
1007 // slide-absolute position
1009 HyperlinkRegions aTranslatedRegions;
1011 // increase capacity to same size as the container for
1012 // shape-relative hyperlink regions to avoid reallocation
1013 aTranslatedRegions.reserve( maHyperlinkRegions.size() );
1014 const basegfx::B2DPoint aOffset(getBounds().getMinimum());
1015 for( const auto& cp : maHyperlinkRegions )
1017 basegfx::B2DRange const& relRegion( cp.first );
1018 aTranslatedRegions.emplace_back(
1019 basegfx::B2DRange(
1020 relRegion.getMinimum() + aOffset,
1021 relRegion.getMaximum() + aOffset),
1022 cp.second );
1025 return aTranslatedRegions;
1028 double DrawShape::getHyperlinkPriority() const
1030 return getPriority();
1034 // AnimatableShape methods
1037 void DrawShape::enterAnimationMode()
1039 OSL_ENSURE( !maViewShapes.empty(),
1040 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1042 if( mnIsAnimatedCount == 0 )
1044 // notify all ViewShapes, by calling their enterAnimationMode method.
1045 // We're now entering animation mode
1046 for( const auto& rViewShape : maViewShapes )
1047 rViewShape->enterAnimationMode();
1050 ++mnIsAnimatedCount;
1053 void DrawShape::leaveAnimationMode()
1055 OSL_ENSURE( !maViewShapes.empty(),
1056 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1058 --mnIsAnimatedCount;
1060 if( mnIsAnimatedCount == 0 )
1062 // notify all ViewShapes, by calling their leaveAnimationMode method.
1063 // we're now leaving animation mode
1064 for( const auto& rViewShape : maViewShapes )
1065 rViewShape->leaveAnimationMode();
1070 // AttributableShape methods
1073 ShapeAttributeLayerSharedPtr DrawShape::createAttributeLayer()
1075 // create new layer, with last as its new child
1076 mpAttributeLayer = std::make_shared<ShapeAttributeLayer>( mpAttributeLayer );
1078 // Update the local state ids to reflect those of the new layer.
1079 updateStateIds();
1081 return mpAttributeLayer;
1084 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr& rLayer )
1086 if( !mpAttributeLayer )
1087 return false; // no layers
1089 if( mpAttributeLayer == rLayer )
1091 // it's the toplevel layer
1092 mpAttributeLayer = mpAttributeLayer->getChildLayer();
1094 // force content redraw, all state variables have
1095 // possibly changed
1096 mbAttributeLayerRevoked = true;
1098 return true;
1100 else
1102 // pass on to the layer, to try its children
1103 return mpAttributeLayer->revokeChildLayer( rLayer );
1107 ShapeAttributeLayerSharedPtr DrawShape::getTopmostAttributeLayer() const
1109 return mpAttributeLayer;
1112 void DrawShape::setVisibility( bool bVisible )
1114 if( mbIsVisible != bVisible )
1116 mbIsVisible = bVisible;
1117 mbForceUpdate = true;
1121 const DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier() const
1123 return *this;
1126 DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier()
1128 return *this;
1131 DocTreeNode DrawShape::getSubsetNode() const
1133 // forward to delegate
1134 return maSubsetting.getSubsetNode();
1137 AttributableShapeSharedPtr DrawShape::getSubset( const DocTreeNode& rTreeNode ) const
1139 // forward to delegate
1140 return maSubsetting.getSubsetShape( rTreeNode );
1143 bool DrawShape::createSubset( AttributableShapeSharedPtr& o_rSubset,
1144 const DocTreeNode& rTreeNode )
1146 // subset shape already created for this DocTreeNode?
1147 AttributableShapeSharedPtr pSubset( maSubsetting.getSubsetShape( rTreeNode ) );
1149 // when true, this method has created a new subset
1150 // DrawShape
1151 bool bNewlyCreated( false );
1153 if( pSubset )
1155 o_rSubset = std::move(pSubset);
1157 // reusing existing subset
1159 else
1161 // not yet created, init entry
1162 o_rSubset.reset( new DrawShape( *this,
1163 rTreeNode,
1164 // TODO(Q3): That's a
1165 // hack. We assume
1166 // that start and end
1167 // index will always
1168 // be less than 65535
1169 mnPriority +
1170 rTreeNode.getStartIndex()/double(SAL_MAX_INT16) ));
1172 bNewlyCreated = true; // subset newly created
1175 // always register shape at DrawShapeSubsetting, to keep
1176 // refcount up-to-date
1177 maSubsetting.addSubsetShape( o_rSubset );
1179 // flush bounds cache
1180 maCurrentShapeUnitBounds.reset();
1182 return bNewlyCreated;
1185 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr& rShape )
1187 // flush bounds cache
1188 maCurrentShapeUnitBounds.reset();
1190 // forward to delegate
1191 if( maSubsetting.revokeSubsetShape( rShape ) )
1193 // force redraw, our content has possibly changed (as
1194 // one of the subsets now display within our shape
1195 // again).
1196 mbForceUpdate = true;
1198 // #i47428# TEMP FIX: synchronize visibility of subset
1199 // with parent.
1201 // TODO(F3): Remove here, and implement
1202 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1203 // additional level of indirection: create a
1204 // persistent subset, containing all text/only the
1205 // background respectively. From _that_ object,
1206 // generate the temporary character subset shapes.
1207 const ShapeAttributeLayerSharedPtr xAttrLayer(
1208 rShape->getTopmostAttributeLayer() );
1209 if( xAttrLayer &&
1210 xAttrLayer->isVisibilityValid() &&
1211 xAttrLayer->getVisibility() != isVisible() )
1213 const bool bVisibility( xAttrLayer->getVisibility() );
1215 // visibilities differ - adjust ours, then
1216 if( mpAttributeLayer )
1217 mpAttributeLayer->setVisibility( bVisibility );
1218 else
1219 mbIsVisible = bVisibility;
1222 // END TEMP FIX
1224 return true;
1227 return false;
1230 sal_Int32 DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1232 return maSubsetting.getNumberOfTreeNodes( eNodeType );
1235 DocTreeNode DrawShape::getTreeNode( sal_Int32 nNodeIndex,
1236 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1238 if ( hasHyperlinks())
1240 prepareHyperlinkIndices();
1243 return maSubsetting.getTreeNode( nNodeIndex, eNodeType );
1246 sal_Int32 DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode& rParentNode,
1247 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1249 return maSubsetting.getNumberOfSubsetTreeNodes( rParentNode, eNodeType );
1252 DocTreeNode DrawShape::getSubsetTreeNode( const DocTreeNode& rParentNode,
1253 sal_Int32 nNodeIndex,
1254 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1256 return maSubsetting.getSubsetTreeNode( rParentNode, nNodeIndex, eNodeType );
1259 void DrawShape::getSomeAnimationFramesFromGraphic(::std::size_t nFrameCount,
1260 ::std::size_t nLastToLoad /* = 0*/)
1262 OSL_ASSERT(mpGraphicLoader);
1264 //load nFrameCount frames or to nLastToLoad
1265 ::std::size_t nFramesToLoad = nFrameCount;
1266 if (nLastToLoad > mpGraphicLoader->mnLoadedFrames + nFrameCount)
1267 nFramesToLoad = nLastToLoad - mpGraphicLoader->mnLoadedFrames;
1269 getAnimationFromGraphic(maAnimationFrames, mnAnimationLoopCount,
1270 mpGraphicLoader->mpGraphic, mpGraphicLoader->mpVDev,
1271 mpGraphicLoader->mpVDevMask, mpGraphicLoader->mnLoadedFrames,
1272 nFramesToLoad);
1274 // If the Animation is fully loaded, no need to load anymore.
1275 if (mpGraphicLoader->mnLoadedFrames >= maAnimationFrames.size())
1277 mpGraphicLoader.reset();
1281 DelayedGraphicLoader::DelayedGraphicLoader(std::shared_ptr<Graphic> pGraphic)
1282 : mpGraphic(std::move(pGraphic))
1283 , mpVDevMask(DeviceFormat::WITHOUT_ALPHA)
1288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */