Bump version to 6.4-15
[LibreOffice.git] / slideshow / source / engine / shapes / drawshape.cxx
blobfa0b99344cc0740b66040e3f8c9bf92879a07433
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 <osl/diagnose.hxx>
23 #include <sal/log.hxx>
24 #include <com/sun/star/awt/Rectangle.hpp>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/awt/FontWeight.hpp>
27 #include <comphelper/anytostring.hxx>
28 #include <cppuhelper/exc_hlp.hxx>
30 #include <vcl/metaact.hxx>
31 #include <vcl/gdimtf.hxx>
32 #include <vcl/graph.hxx>
34 #include <basegfx/numeric/ftools.hxx>
36 #include <rtl/math.hxx>
38 #include <com/sun/star/drawing/TextAnimationKind.hpp>
40 #include <vcl/svapp.hxx>
41 #include <vcl/window.hxx>
42 #include <tools/stream.hxx>
43 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
44 #include <com/sun/star/datatransfer/XTransferable.hpp>
46 #include <comphelper/scopeguard.hxx>
47 #include <canvas/canvastools.hxx>
49 #include <cmath>
50 #include <algorithm>
51 #include <iterator>
52 #include <functional>
53 #include <limits>
55 #include "drawshapesubsetting.hxx"
56 #include "drawshape.hxx"
57 #include <eventqueue.hxx>
58 #include <wakeupevent.hxx>
59 #include <subsettableshapemanager.hxx>
60 #include "intrinsicanimationactivity.hxx"
61 #include <slideshowexceptions.hxx>
62 #include <tools.hxx>
63 #include "gdimtftools.hxx"
64 #include "drawinglayeranimation.hxx"
66 #include <math.h>
68 using namespace ::com::sun::star;
71 namespace slideshow
73 namespace internal
77 // Private methods
80 GDIMetaFileSharedPtr const & DrawShape::forceScrollTextMetaFile()
82 if ((mnCurrMtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != MTF_LOAD_SCROLL_TEXT_MTF)
84 // reload with added flags:
85 mnCurrMtfLoadFlags |= MTF_LOAD_SCROLL_TEXT_MTF;
86 mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY),
87 mxPage, mnCurrMtfLoadFlags,
88 mxComponentContext);
90 if (!mpCurrMtf)
91 mpCurrMtf.reset( new GDIMetaFile );
93 // TODO(F1): Currently, the scroll metafile will
94 // never contain any verbose text comments. Thus,
95 // can only display the full mtf content, no
96 // subsets.
97 maSubsetting.reset( mpCurrMtf );
99 // adapt maBounds. the requested scroll text metafile
100 // will typically have dimension different from the
101 // actual shape
102 ::basegfx::B2DRectangle aScrollRect, aPaintRect;
103 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect,
104 aPaintRect,
105 mpCurrMtf ),
106 "DrawShape::forceScrollTextMetaFile(): Could "
107 "not extract scroll anim rectangles from mtf" );
109 // take the larger one of the two rectangles (that
110 // should be the bound rect of the retrieved
111 // metafile)
112 if( aScrollRect.isInside( aPaintRect ) )
113 maBounds = aScrollRect;
114 else
115 maBounds = aPaintRect;
117 return mpCurrMtf;
120 void DrawShape::updateStateIds() const
122 // Update the states, we've just redrawn or created a new
123 // attribute layer.
124 if( mpAttributeLayer )
126 mnAttributeTransformationState = mpAttributeLayer->getTransformationState();
127 mnAttributeClipState = mpAttributeLayer->getClipState();
128 mnAttributeAlphaState = mpAttributeLayer->getAlphaState();
129 mnAttributePositionState = mpAttributeLayer->getPositionState();
130 mnAttributeContentState = mpAttributeLayer->getContentState();
131 mnAttributeVisibilityState = mpAttributeLayer->getVisibilityState();
135 ViewShape::RenderArgs DrawShape::getViewRenderArgs() const
137 return ViewShape::RenderArgs(
138 maBounds,
139 getUpdateArea(),
140 getBounds(),
141 getActualUnitShapeBounds(),
142 mpAttributeLayer,
143 maSubsetting.getActiveSubsets(),
144 mnPriority);
147 bool DrawShape::implRender( UpdateFlags nUpdateFlags ) const
149 SAL_INFO( "slideshow", "::presentation::internal::DrawShape::implRender()" );
150 SAL_INFO( "slideshow", "::presentation::internal::DrawShape: 0x" << std::hex << this );
152 // will perform the update now, clear update-enforcing
153 // flags
154 mbForceUpdate = false;
155 mbAttributeLayerRevoked = false;
157 ENSURE_OR_RETURN_FALSE( !maViewShapes.empty(),
158 "DrawShape::implRender(): render called on DrawShape without views" );
160 if( maBounds.isEmpty() )
162 // zero-sized shapes are effectively invisible,
163 // thus, we save us the rendering...
164 return true;
167 // redraw all view shapes, by calling their update() method
168 ViewShape::RenderArgs renderArgs( getViewRenderArgs() );
169 bool bVisible = isVisible();
170 if( ::std::count_if( maViewShapes.begin(),
171 maViewShapes.end(),
172 [this, &bVisible, &renderArgs, &nUpdateFlags]
173 ( const ViewShapeSharedPtr& pShape )
174 { return pShape->update( this->mpCurrMtf,
175 renderArgs,
176 nUpdateFlags,
177 bVisible ); } )
178 != static_cast<ViewShapeVector::difference_type>(maViewShapes.size()) )
180 // at least one of the ViewShape::update() calls did return
181 // false - update failed on at least one ViewLayer
182 return false;
185 // successfully redrawn - update state IDs to detect next changes
186 updateStateIds();
188 return true;
191 UpdateFlags DrawShape::getUpdateFlags() const
193 // default: update nothing, unless ShapeAttributeStack
194 // tells us below, or if the attribute layer was revoked
195 UpdateFlags nUpdateFlags(UpdateFlags::NONE);
197 // possibly the whole shape content changed
198 if( mbAttributeLayerRevoked )
199 nUpdateFlags = UpdateFlags::Content;
202 // determine what has to be updated
205 // do we have an attribute layer?
206 if( mpAttributeLayer )
208 // Prevent nUpdateFlags to be modified when the shape is not
209 // visible, except when it just was hidden.
210 if (mpAttributeLayer->getVisibility()
211 || mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
213 if (mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
215 // Change of the visibility state is mapped to
216 // content change because when the visibility
217 // changes then usually a sprite is shown or hidden
218 // and the background under has to be painted once.
219 nUpdateFlags |= UpdateFlags::Content;
222 // TODO(P1): This can be done without conditional branching.
223 // See HAKMEM.
224 if( mpAttributeLayer->getPositionState() != mnAttributePositionState )
226 nUpdateFlags |= UpdateFlags::Position;
228 if( mpAttributeLayer->getAlphaState() != mnAttributeAlphaState )
230 nUpdateFlags |= UpdateFlags::Alpha;
232 if( mpAttributeLayer->getClipState() != mnAttributeClipState )
234 nUpdateFlags |= UpdateFlags::Clip;
236 if( mpAttributeLayer->getTransformationState() != mnAttributeTransformationState )
238 nUpdateFlags |= UpdateFlags::Transformation;
240 if( mpAttributeLayer->getContentState() != mnAttributeContentState )
242 nUpdateFlags |= UpdateFlags::Content;
247 return nUpdateFlags;
250 ::basegfx::B2DRectangle DrawShape::getActualUnitShapeBounds() const
252 ENSURE_OR_THROW( !maViewShapes.empty(),
253 "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
255 const VectorOfDocTreeNodes& rSubsets(
256 maSubsetting.getActiveSubsets() );
258 const ::basegfx::B2DRectangle aDefaultBounds( 0.0,0.0,1.0,1.0 );
260 // perform the cheapest check first
261 if( rSubsets.empty() )
263 // if subset contains the whole shape, no need to call
264 // the somewhat expensive bound calculation, since as
265 // long as the subset is empty, this branch will be
266 // taken.
267 return aDefaultBounds;
269 else
271 OSL_ENSURE( rSubsets.size() != 1 ||
272 !rSubsets.front().isEmpty(),
273 "DrawShape::getActualUnitShapeBounds() expects a "
274 "_non-empty_ subset vector for a subsetted shape!" );
276 // are the cached bounds still valid?
277 if( !maCurrentShapeUnitBounds )
279 // no, (re)generate them
280 // =====================
282 // setup cached values to defaults (might fail to
283 // retrieve true bounds below)
284 maCurrentShapeUnitBounds.reset( aDefaultBounds );
286 // TODO(P2): the subset of the master shape (that from
287 // which the subsets are subtracted) changes
288 // relatively often (every time a subset shape is
289 // added or removed). Maybe we should exclude it here,
290 // always assuming full bounds?
292 ::cppcanvas::CanvasSharedPtr pDestinationCanvas(
293 maViewShapes.front()->getViewLayer()->getCanvas() );
295 // TODO(Q2): Although this _is_ currently
296 // view-agnostic, it might not stay like
297 // that. Maybe this method should again be moved
298 // to the ViewShape
299 ::cppcanvas::RendererSharedPtr pRenderer(
300 maViewShapes.front()->getRenderer(
301 pDestinationCanvas, mpCurrMtf, mpAttributeLayer ) );
303 // If we cannot not prefetch, be defensive and assume
304 // full shape size
305 if( pRenderer )
307 // temporarily, switch total transformation to identity
308 // (need the bounds in the [0,1]x[0,1] unit coordinate
309 // system.
310 ::basegfx::B2DHomMatrix aEmptyTransformation;
312 ::basegfx::B2DHomMatrix aOldTransform( pDestinationCanvas->getTransformation() );
313 pDestinationCanvas->setTransformation( aEmptyTransformation );
314 pRenderer->setTransformation( aEmptyTransformation );
316 // restore old transformation when leaving the scope
317 const ::comphelper::ScopeGuard aGuard(
318 [&pDestinationCanvas, &aOldTransform]()
319 { return pDestinationCanvas->setTransformation( aOldTransform ); } );
322 // retrieve bounds for subset of whole metafile
325 ::basegfx::B2DRange aTotalBounds;
327 // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
328 // is overloaded.
329 for( const auto& rDocTreeNode : rSubsets )
330 aTotalBounds.expand( pRenderer->getSubsetArea(
331 rDocTreeNode.getStartIndex(),
332 rDocTreeNode.getEndIndex() ) );
334 OSL_ENSURE( aTotalBounds.getMinX() >= -0.1 &&
335 aTotalBounds.getMinY() >= -0.1 &&
336 aTotalBounds.getMaxX() <= 1.1 &&
337 aTotalBounds.getMaxY() <= 1.1,
338 "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
340 // really make sure no shape appears larger than its
341 // original bounds (there _are_ some pathologic cases,
342 // especially when imported from PPT, that have
343 // e.g. obscenely large polygon bounds)
344 aTotalBounds.intersect(
345 ::basegfx::B2DRange( 0.0, 0.0,
346 1.0, 1.0 ));
348 maCurrentShapeUnitBounds.reset( aTotalBounds );
352 return *maCurrentShapeUnitBounds;
356 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
357 const uno::Reference< drawing::XDrawPage >& xContainingPage,
358 double nPrio,
359 bool bForeignSource,
360 const SlideShowContext& rContext ) :
361 mxShape( xShape ),
362 mxPage( xContainingPage ),
363 maAnimationFrames(), // empty, we don't have no intrinsic animation
364 mnCurrFrame(0),
365 mpCurrMtf(),
366 mnCurrMtfLoadFlags( bForeignSource
367 ? MTF_LOAD_FOREIGN_SOURCE : MTF_LOAD_NONE ),
368 maCurrentShapeUnitBounds(),
369 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
370 maBounds( getAPIShapeBounds( xShape ) ),
371 mpAttributeLayer(),
372 mpIntrinsicAnimationActivity(),
373 mnAttributeTransformationState(0),
374 mnAttributeClipState(0),
375 mnAttributeAlphaState(0),
376 mnAttributePositionState(0),
377 mnAttributeContentState(0),
378 mnAttributeVisibilityState(0),
379 maViewShapes(),
380 mxComponentContext( rContext.mxComponentContext ),
381 maHyperlinkIndices(),
382 maHyperlinkRegions(),
383 maSubsetting(),
384 mnIsAnimatedCount(0),
385 mnAnimationLoopCount(0),
386 mbIsVisible( true ),
387 mbForceUpdate( false ),
388 mbAttributeLayerRevoked( false ),
389 mbDrawingLayerAnim( false )
391 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
392 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
394 // check for drawing layer animations:
395 drawing::TextAnimationKind eKind = drawing::TextAnimationKind_NONE;
396 uno::Reference<beans::XPropertySet> xPropSet( mxShape,
397 uno::UNO_QUERY );
398 if( xPropSet.is() )
399 getPropertyValue( eKind, xPropSet,
400 "TextAnimationKind" );
401 mbDrawingLayerAnim = (eKind != drawing::TextAnimationKind_NONE);
403 // must NOT be called from within initializer list, uses
404 // state from mnCurrMtfLoadFlags!
405 mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY),
406 xContainingPage, mnCurrMtfLoadFlags,
407 mxComponentContext );
408 if (!mpCurrMtf)
409 mpCurrMtf.reset(new GDIMetaFile);
411 maSubsetting.reset( mpCurrMtf );
413 prepareHyperlinkIndices();
416 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
417 const uno::Reference< drawing::XDrawPage >& xContainingPage,
418 double nPrio,
419 const Graphic& rGraphic,
420 const SlideShowContext& rContext ) :
421 mxShape( xShape ),
422 mxPage( xContainingPage ),
423 maAnimationFrames(),
424 mnCurrFrame(0),
425 mpCurrMtf(),
426 mnCurrMtfLoadFlags( MTF_LOAD_NONE ),
427 maCurrentShapeUnitBounds(),
428 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
429 maBounds( getAPIShapeBounds( xShape ) ),
430 mpAttributeLayer(),
431 mpIntrinsicAnimationActivity(),
432 mnAttributeTransformationState(0),
433 mnAttributeClipState(0),
434 mnAttributeAlphaState(0),
435 mnAttributePositionState(0),
436 mnAttributeContentState(0),
437 mnAttributeVisibilityState(0),
438 maViewShapes(),
439 mxComponentContext( rContext.mxComponentContext ),
440 maHyperlinkIndices(),
441 maHyperlinkRegions(),
442 maSubsetting(),
443 mnIsAnimatedCount(0),
444 mnAnimationLoopCount(0),
445 mbIsVisible( true ),
446 mbForceUpdate( false ),
447 mbAttributeLayerRevoked( false ),
448 mbDrawingLayerAnim( false )
450 ENSURE_OR_THROW( rGraphic.IsAnimated(),
451 "DrawShape::DrawShape(): Graphic is no animation" );
453 getAnimationFromGraphic( maAnimationFrames,
454 mnAnimationLoopCount,
455 rGraphic );
457 ENSURE_OR_THROW( !maAnimationFrames.empty() &&
458 maAnimationFrames.front().mpMtf,
459 "DrawShape::DrawShape(): " );
460 mpCurrMtf = maAnimationFrames.front().mpMtf;
462 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
463 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
464 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
467 DrawShape::DrawShape( const DrawShape& rSrc,
468 const DocTreeNode& rTreeNode,
469 double nPrio ) :
470 mxShape( rSrc.mxShape ),
471 mxPage( rSrc.mxPage ),
472 maAnimationFrames(), // don't copy animations for subsets,
473 // only the current frame!
474 mnCurrFrame(0),
475 mpCurrMtf( rSrc.mpCurrMtf ),
476 mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ),
477 maCurrentShapeUnitBounds(),
478 mnPriority( nPrio ),
479 maBounds( rSrc.maBounds ),
480 mpAttributeLayer(),
481 mpIntrinsicAnimationActivity(),
482 mnAttributeTransformationState(0),
483 mnAttributeClipState(0),
484 mnAttributeAlphaState(0),
485 mnAttributePositionState(0),
486 mnAttributeContentState(0),
487 mnAttributeVisibilityState(0),
488 maViewShapes(),
489 mxComponentContext( rSrc.mxComponentContext ),
490 maHyperlinkIndices(),
491 maHyperlinkRegions(),
492 maSubsetting( rTreeNode, mpCurrMtf ),
493 mnIsAnimatedCount(0),
494 mnAnimationLoopCount(0),
495 mbIsVisible( rSrc.mbIsVisible ),
496 mbForceUpdate( false ),
497 mbAttributeLayerRevoked( false ),
498 mbDrawingLayerAnim( false )
500 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
501 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
503 // xxx todo: currently not implemented for subsetted shapes;
504 // would mean modifying set of hyperlink regions when
505 // subsetting text portions. N.B.: there's already an
506 // issue for this #i72828#
510 // Public methods
513 DrawShapeSharedPtr DrawShape::create(
514 const uno::Reference< drawing::XShape >& xShape,
515 const uno::Reference< drawing::XDrawPage >& xContainingPage,
516 double nPrio,
517 bool bForeignSource,
518 const SlideShowContext& rContext )
520 DrawShapeSharedPtr pShape( new DrawShape(xShape,
521 xContainingPage,
522 nPrio,
523 bForeignSource,
524 rContext) );
526 if( pShape->hasIntrinsicAnimation() )
528 OSL_ASSERT( pShape->maAnimationFrames.empty() );
529 if( pShape->getNumberOfTreeNodes(
530 DocTreeNode::NodeType::LogicalParagraph) > 0 )
532 pShape->mpIntrinsicAnimationActivity =
533 createDrawingLayerAnimActivity(
534 rContext,
535 pShape);
539 if( pShape->hasHyperlinks() )
540 rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape );
542 return pShape;
545 DrawShapeSharedPtr DrawShape::create(
546 const uno::Reference< drawing::XShape >& xShape,
547 const uno::Reference< drawing::XDrawPage >& xContainingPage,
548 double nPrio,
549 const Graphic& rGraphic,
550 const SlideShowContext& rContext )
552 DrawShapeSharedPtr pShape( new DrawShape(xShape,
553 xContainingPage,
554 nPrio,
555 rGraphic,
556 rContext) );
558 if( pShape->hasIntrinsicAnimation() )
560 OSL_ASSERT( !pShape->maAnimationFrames.empty() );
562 std::vector<double> aTimeout;
563 std::transform(
564 pShape->maAnimationFrames.begin(),
565 pShape->maAnimationFrames.end(),
566 std::back_insert_iterator< std::vector<double> >( aTimeout ),
567 std::mem_fn(&MtfAnimationFrame::getDuration) );
569 WakeupEventSharedPtr pWakeupEvent(
570 new WakeupEvent( rContext.mrEventQueue.getTimer(),
571 rContext.mrActivitiesQueue ) );
573 ActivitySharedPtr pActivity =
574 createIntrinsicAnimationActivity(
575 rContext,
576 pShape,
577 pWakeupEvent,
578 aTimeout,
579 pShape->mnAnimationLoopCount);
581 pWakeupEvent->setActivity( pActivity );
582 pShape->mpIntrinsicAnimationActivity = pActivity;
585 OSL_ENSURE( !pShape->hasHyperlinks(),
586 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
588 return pShape;
591 DrawShape::~DrawShape()
595 // dispose intrinsic animation activity, else, it will
596 // linger forever
597 ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() );
598 if( pActivity )
599 pActivity->dispose();
601 catch (uno::Exception const &)
603 DBG_UNHANDLED_EXCEPTION("slideshow");
607 uno::Reference< drawing::XShape > DrawShape::getXShape() const
609 return mxShape;
612 void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer,
613 bool bRedrawLayer )
615 // already added?
616 if( ::std::any_of( maViewShapes.begin(),
617 maViewShapes.end(),
618 [&rNewLayer]
619 ( const ViewShapeSharedPtr& pShape )
620 { return rNewLayer == pShape->getViewLayer(); } ) )
622 // yes, nothing to do
623 return;
626 ViewShapeSharedPtr pNewShape( new ViewShape( rNewLayer ) );
628 maViewShapes.push_back( pNewShape );
630 // pass on animation state
631 if( mnIsAnimatedCount )
633 for( int i=0; i<mnIsAnimatedCount; ++i )
634 pNewShape->enterAnimationMode();
637 // render the Shape on the newly added ViewLayer
638 if( bRedrawLayer )
640 pNewShape->update( mpCurrMtf,
641 getViewRenderArgs(),
642 UpdateFlags::Force,
643 isVisible() );
647 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer )
649 const ViewShapeVector::iterator aEnd( maViewShapes.end() );
651 OSL_ENSURE( ::std::count_if(maViewShapes.begin(),
652 aEnd,
653 [&rLayer]
654 ( const ViewShapeSharedPtr& pShape )
655 { return rLayer == pShape->getViewLayer(); } ) < 2,
656 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
658 ViewShapeVector::iterator aIter;
660 if( (aIter=::std::remove_if( maViewShapes.begin(),
661 aEnd,
662 [&rLayer]
663 ( const ViewShapeSharedPtr& pShape )
664 { return rLayer == pShape->getViewLayer(); } ) ) == aEnd )
666 // view layer seemingly was not added, failed
667 return false;
670 // actually erase from container
671 maViewShapes.erase( aIter, aEnd );
673 return true;
676 void DrawShape::clearAllViewLayers()
678 maViewShapes.clear();
681 bool DrawShape::update() const
683 if( mbForceUpdate )
685 return render();
687 else
689 return implRender( getUpdateFlags() );
693 bool DrawShape::render() const
695 // force redraw. Have to also pass on the update flags,
696 // because e.g. content update (regeneration of the
697 // metafile renderer) is normally not performed. A simple
698 // UpdateFlags::Force would only paint the metafile in its
699 // old state.
700 return implRender( UpdateFlags::Force | getUpdateFlags() );
703 bool DrawShape::isContentChanged() const
705 return mbForceUpdate ||
706 getUpdateFlags() != UpdateFlags::NONE;
710 ::basegfx::B2DRectangle DrawShape::getBounds() const
712 // little optimization: for non-modified shapes, we don't
713 // create an ShapeAttributeStack, and therefore also don't
714 // have to check it.
715 return getShapePosSize( maBounds,
716 mpAttributeLayer );
719 ::basegfx::B2DRectangle DrawShape::getDomBounds() const
721 return maBounds;
724 ::basegfx::B2DRectangle DrawShape::getUpdateArea() const
726 ::basegfx::B2DRectangle aBounds;
728 // an already empty shape bound need no further
729 // treatment. In fact, any changes applied below would
730 // actually remove the special empty state, thus, don't
731 // change!
732 if( !maBounds.isEmpty() )
734 basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0);
736 if( !maViewShapes.empty() )
737 aUnitBounds = getActualUnitShapeBounds();
739 if( !aUnitBounds.isEmpty() )
741 if( mpAttributeLayer )
743 // calc actual shape area (in user coordinate
744 // space) from the transformation as given by the
745 // shape attribute layer
746 aBounds = getShapeUpdateArea( aUnitBounds,
747 getShapeTransformation( getBounds(),
748 mpAttributeLayer ),
749 mpAttributeLayer );
751 else
753 // no attribute layer, thus, the true shape bounds
754 // can be directly derived from the XShape bound
755 // attribute
756 aBounds = getShapeUpdateArea( aUnitBounds,
757 maBounds );
760 if( !maViewShapes.empty() )
762 // determine border needed for antialiasing the shape
763 ::basegfx::B2DSize aAABorder(0.0,0.0);
765 // for every view, get AA border and 'expand' aAABorder
766 // appropriately.
767 for( const auto& rViewShape : maViewShapes )
769 const ::basegfx::B2DSize rShapeBorder( rViewShape->getAntialiasingBorder() );
771 aAABorder.setX( ::std::max(
772 rShapeBorder.getX(),
773 aAABorder.getX() ) );
774 aAABorder.setY( ::std::max(
775 rShapeBorder.getY(),
776 aAABorder.getY() ) );
779 // add calculated AA border to aBounds
780 aBounds = ::basegfx::B2DRectangle( aBounds.getMinX() - aAABorder.getX(),
781 aBounds.getMinY() - aAABorder.getY(),
782 aBounds.getMaxX() + aAABorder.getX(),
783 aBounds.getMaxY() + aAABorder.getY() );
788 return aBounds;
791 bool DrawShape::isVisible() const
793 bool bIsVisible( mbIsVisible );
795 if( mpAttributeLayer )
797 // check whether visibility and alpha are not default
798 // (mpAttributeLayer->isVisibilityValid() returns true
799 // then): bVisible becomes true, if shape visibility
800 // is on and alpha is not 0.0 (fully transparent)
801 if( mpAttributeLayer->isVisibilityValid() )
802 bIsVisible = mpAttributeLayer->getVisibility();
804 // only touch bIsVisible, if the shape is still
805 // visible - if getVisibility already made us
806 // invisible, no alpha value will make us appear
807 // again.
808 if( bIsVisible && mpAttributeLayer->isAlphaValid() )
809 bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
812 return bIsVisible;
815 double DrawShape::getPriority() const
817 return mnPriority;
820 bool DrawShape::isBackgroundDetached() const
822 return mnIsAnimatedCount > 0;
825 bool DrawShape::hasIntrinsicAnimation() const
827 return (!maAnimationFrames.empty() || mbDrawingLayerAnim);
830 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame )
832 ENSURE_OR_RETURN_VOID( nCurrFrame < maAnimationFrames.size(),
833 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
835 if( mnCurrFrame != nCurrFrame )
837 mnCurrFrame = nCurrFrame;
838 mpCurrMtf = maAnimationFrames[ mnCurrFrame ].mpMtf;
839 mbForceUpdate = true;
843 // hyperlink support
844 void DrawShape::prepareHyperlinkIndices() const
846 if ( !maHyperlinkIndices.empty())
848 maHyperlinkIndices.clear();
849 maHyperlinkRegions.clear();
852 sal_Int32 nIndex = 0;
853 for ( MetaAction * pCurrAct = mpCurrMtf->FirstAction();
854 pCurrAct != nullptr; pCurrAct = mpCurrMtf->NextAction() )
856 if (pCurrAct->GetType() == MetaActionType::COMMENT) {
857 MetaCommentAction * pAct =
858 static_cast<MetaCommentAction *>(pCurrAct);
859 // skip comment if not a special XTEXT comment
860 if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
861 // e.g. date field doesn't have data!
862 // currently assuming that only url field, this is
863 // somehow fragile! xxx todo if possible
864 pAct->GetData() != nullptr &&
865 pAct->GetDataSize() > 0)
867 if (!maHyperlinkIndices.empty() &&
868 maHyperlinkIndices.back().second == -1) {
869 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
870 maHyperlinkIndices.pop_back();
871 maHyperlinkRegions.pop_back();
873 maHyperlinkIndices.emplace_back( nIndex + 1,
874 -1 /* to be filled below */ );
875 maHyperlinkRegions.emplace_back(
876 basegfx::B2DRectangle(),
877 OUString(
878 reinterpret_cast<sal_Unicode const*>(
879 pAct->GetData()),
880 pAct->GetDataSize() / sizeof(sal_Unicode) )
883 else if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
884 // pending end is expected:
885 !maHyperlinkIndices.empty() &&
886 maHyperlinkIndices.back().second == -1)
888 maHyperlinkIndices.back().second = nIndex;
890 ++nIndex;
892 else
893 nIndex += getNextActionOffset(pCurrAct);
895 if (!maHyperlinkIndices.empty() &&
896 maHyperlinkIndices.back().second == -1) {
897 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
898 maHyperlinkIndices.pop_back();
899 maHyperlinkRegions.pop_back();
901 OSL_ASSERT( maHyperlinkIndices.size() == maHyperlinkRegions.size());
904 bool DrawShape::hasHyperlinks() const
906 return ! maHyperlinkRegions.empty();
909 HyperlinkArea::HyperlinkRegions DrawShape::getHyperlinkRegions() const
911 OSL_ASSERT( !maViewShapes.empty() );
913 if( !isVisible() )
914 return HyperlinkArea::HyperlinkRegions();
916 // late init, determine regions:
917 if( !maHyperlinkRegions.empty() &&
918 !maViewShapes.empty() &&
919 // region already inited?
920 maHyperlinkRegions.front().first.getWidth() == 0 &&
921 maHyperlinkRegions.front().first.getHeight() == 0 &&
922 maHyperlinkRegions.size() == maHyperlinkIndices.size() )
924 // TODO(Q2): Although this _is_ currently
925 // view-agnostic, it might not stay like that.
926 ViewShapeSharedPtr const& pViewShape = maViewShapes.front();
927 cppcanvas::CanvasSharedPtr const pCanvas(
928 pViewShape->getViewLayer()->getCanvas() );
930 // reuse Renderer of first view shape:
931 cppcanvas::RendererSharedPtr const pRenderer(
932 pViewShape->getRenderer(
933 pCanvas, mpCurrMtf, mpAttributeLayer ) );
935 OSL_ASSERT( pRenderer );
937 if (pRenderer)
939 basegfx::B2DHomMatrix const aOldTransform(
940 pCanvas->getTransformation() );
941 basegfx::B2DHomMatrix aTransform;
942 pCanvas->setTransformation( aTransform /* empty */ );
945 ::cppcanvas::Canvas* pTmpCanvas = pCanvas.get();
946 comphelper::ScopeGuard const resetOldTransformation(
947 [&aOldTransform, &pTmpCanvas]()
948 { return pTmpCanvas->setTransformation( aOldTransform ); } );
950 aTransform.scale( maBounds.getWidth(),
951 maBounds.getHeight() );
952 pRenderer->setTransformation( aTransform );
953 pRenderer->setClip();
955 for( std::size_t pos = maHyperlinkRegions.size(); pos--; )
957 // get region:
958 HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos];
959 basegfx::B2DRectangle const region(
960 pRenderer->getSubsetArea( rIndices.first,
961 rIndices.second ));
962 maHyperlinkRegions[pos].first = region;
967 // shift shape-relative hyperlink regions to
968 // slide-absolute position
970 HyperlinkRegions aTranslatedRegions;
972 // increase capacity to same size as the container for
973 // shape-relative hyperlink regions to avoid reallocation
974 aTranslatedRegions.reserve( maHyperlinkRegions.size() );
975 const basegfx::B2DPoint& rOffset(getBounds().getMinimum());
976 for( const auto& cp : maHyperlinkRegions )
978 basegfx::B2DRange const& relRegion( cp.first );
979 aTranslatedRegions.emplace_back(
980 basegfx::B2DRange(
981 relRegion.getMinimum() + rOffset,
982 relRegion.getMaximum() + rOffset),
983 cp.second );
986 return aTranslatedRegions;
989 double DrawShape::getHyperlinkPriority() const
991 return getPriority();
995 // AnimatableShape methods
998 void DrawShape::enterAnimationMode()
1000 OSL_ENSURE( !maViewShapes.empty(),
1001 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1003 if( mnIsAnimatedCount == 0 )
1005 // notify all ViewShapes, by calling their enterAnimationMode method.
1006 // We're now entering animation mode
1007 for( const auto& rViewShape : maViewShapes )
1008 rViewShape->enterAnimationMode();
1011 ++mnIsAnimatedCount;
1014 void DrawShape::leaveAnimationMode()
1016 OSL_ENSURE( !maViewShapes.empty(),
1017 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1019 --mnIsAnimatedCount;
1021 if( mnIsAnimatedCount == 0 )
1023 // notify all ViewShapes, by calling their leaveAnimationMode method.
1024 // we're now leaving animation mode
1025 for( const auto& rViewShape : maViewShapes )
1026 rViewShape->leaveAnimationMode();
1031 // AttributableShape methods
1034 ShapeAttributeLayerSharedPtr DrawShape::createAttributeLayer()
1036 // create new layer, with last as its new child
1037 mpAttributeLayer.reset( new ShapeAttributeLayer( mpAttributeLayer ) );
1039 // Update the local state ids to reflect those of the new layer.
1040 updateStateIds();
1042 return mpAttributeLayer;
1045 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr& rLayer )
1047 if( !mpAttributeLayer )
1048 return false; // no layers
1050 if( mpAttributeLayer == rLayer )
1052 // it's the toplevel layer
1053 mpAttributeLayer = mpAttributeLayer->getChildLayer();
1055 // force content redraw, all state variables have
1056 // possibly changed
1057 mbAttributeLayerRevoked = true;
1059 return true;
1061 else
1063 // pass on to the layer, to try its children
1064 return mpAttributeLayer->revokeChildLayer( rLayer );
1068 ShapeAttributeLayerSharedPtr DrawShape::getTopmostAttributeLayer() const
1070 return mpAttributeLayer;
1073 void DrawShape::setVisibility( bool bVisible )
1075 if( mbIsVisible != bVisible )
1077 mbIsVisible = bVisible;
1078 mbForceUpdate = true;
1082 const DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier() const
1084 return *this;
1087 DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier()
1089 return *this;
1092 DocTreeNode DrawShape::getSubsetNode() const
1094 // forward to delegate
1095 return maSubsetting.getSubsetNode();
1098 AttributableShapeSharedPtr DrawShape::getSubset( const DocTreeNode& rTreeNode ) const
1100 // forward to delegate
1101 return maSubsetting.getSubsetShape( rTreeNode );
1104 bool DrawShape::createSubset( AttributableShapeSharedPtr& o_rSubset,
1105 const DocTreeNode& rTreeNode )
1107 // subset shape already created for this DocTreeNode?
1108 AttributableShapeSharedPtr pSubset( maSubsetting.getSubsetShape( rTreeNode ) );
1110 // when true, this method has created a new subset
1111 // DrawShape
1112 bool bNewlyCreated( false );
1114 if( pSubset )
1116 o_rSubset = pSubset;
1118 // reusing existing subset
1120 else
1122 // not yet created, init entry
1123 o_rSubset.reset( new DrawShape( *this,
1124 rTreeNode,
1125 // TODO(Q3): That's a
1126 // hack. We assume
1127 // that start and end
1128 // index will always
1129 // be less than 65535
1130 mnPriority +
1131 rTreeNode.getStartIndex()/double(SAL_MAX_INT16) ));
1133 bNewlyCreated = true; // subset newly created
1136 // always register shape at DrawShapeSubsetting, to keep
1137 // refcount up-to-date
1138 maSubsetting.addSubsetShape( o_rSubset );
1140 // flush bounds cache
1141 maCurrentShapeUnitBounds.reset();
1143 return bNewlyCreated;
1146 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr& rShape )
1148 // flush bounds cache
1149 maCurrentShapeUnitBounds.reset();
1151 // forward to delegate
1152 if( maSubsetting.revokeSubsetShape( rShape ) )
1154 // force redraw, our content has possibly changed (as
1155 // one of the subsets now display within our shape
1156 // again).
1157 mbForceUpdate = true;
1159 // #i47428# TEMP FIX: synchronize visibility of subset
1160 // with parent.
1162 // TODO(F3): Remove here, and implement
1163 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1164 // additional level of indirection: create a
1165 // persistent subset, containing all text/only the
1166 // background respectively. From _that_ object,
1167 // generate the temporary character subset shapes.
1168 const ShapeAttributeLayerSharedPtr& rAttrLayer(
1169 rShape->getTopmostAttributeLayer() );
1170 if( rAttrLayer &&
1171 rAttrLayer->isVisibilityValid() &&
1172 rAttrLayer->getVisibility() != isVisible() )
1174 const bool bVisibility( rAttrLayer->getVisibility() );
1176 // visibilities differ - adjust ours, then
1177 if( mpAttributeLayer )
1178 mpAttributeLayer->setVisibility( bVisibility );
1179 else
1180 mbIsVisible = bVisibility;
1183 // END TEMP FIX
1185 return true;
1188 return false;
1191 sal_Int32 DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1193 return maSubsetting.getNumberOfTreeNodes( eNodeType );
1196 DocTreeNode DrawShape::getTreeNode( sal_Int32 nNodeIndex,
1197 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1199 if ( hasHyperlinks())
1201 prepareHyperlinkIndices();
1204 return maSubsetting.getTreeNode( nNodeIndex, eNodeType );
1207 sal_Int32 DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode& rParentNode,
1208 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1210 return maSubsetting.getNumberOfSubsetTreeNodes( rParentNode, eNodeType );
1213 DocTreeNode DrawShape::getSubsetTreeNode( const DocTreeNode& rParentNode,
1214 sal_Int32 nNodeIndex,
1215 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1217 return maSubsetting.getSubsetTreeNode( rParentNode, nNodeIndex, eNodeType );
1222 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */