Bump version to 6.0-36
[LibreOffice.git] / slideshow / source / engine / shapes / drawshape.cxx
blob00a8894f5ef0f70c9eacb4d5680ef42adcfde930
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 <com/sun/star/awt/Rectangle.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/awt/FontWeight.hpp>
26 #include <comphelper/anytostring.hxx>
27 #include <cppuhelper/exc_hlp.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/gdimtf.hxx>
31 #include <vcl/wrkwin.hxx>
33 #include <basegfx/numeric/ftools.hxx>
35 #include <rtl/math.hxx>
37 #include <com/sun/star/drawing/TextAnimationKind.hpp>
39 #include <vcl/svapp.hxx>
40 #include <vcl/window.hxx>
41 #include <tools/stream.hxx>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/datatransfer/XTransferable.hpp>
45 #include <comphelper/scopeguard.hxx>
46 #include <canvas/canvastools.hxx>
48 #include <cmath>
49 #include <algorithm>
50 #include <iterator>
51 #include <functional>
52 #include <limits>
54 #include "drawshapesubsetting.hxx"
55 #include "drawshape.hxx"
56 #include <eventqueue.hxx>
57 #include <wakeupevent.hxx>
58 #include <subsettableshapemanager.hxx>
59 #include "intrinsicanimationactivity.hxx"
60 #include <slideshowexceptions.hxx>
61 #include <tools.hxx>
62 #include "gdimtftools.hxx"
63 #include "drawinglayeranimation.hxx"
65 #include <math.h>
67 using namespace ::com::sun::star;
70 namespace slideshow
72 namespace internal
76 // Private methods
79 GDIMetaFileSharedPtr const & DrawShape::forceScrollTextMetaFile()
81 if ((mnCurrMtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != MTF_LOAD_SCROLL_TEXT_MTF)
83 // reload with added flags:
84 mnCurrMtfLoadFlags |= MTF_LOAD_SCROLL_TEXT_MTF;
85 mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY),
86 mxPage, mnCurrMtfLoadFlags,
87 mxComponentContext);
89 if (!mpCurrMtf)
90 mpCurrMtf.reset( new GDIMetaFile );
92 // TODO(F1): Currently, the scroll metafile will
93 // never contain any verbose text comments. Thus,
94 // can only display the full mtf content, no
95 // subsets.
96 maSubsetting.reset( mpCurrMtf );
98 // adapt maBounds. the requested scroll text metafile
99 // will typically have dimension different from the
100 // actual shape
101 ::basegfx::B2DRectangle aScrollRect, aPaintRect;
102 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect,
103 aPaintRect,
104 mpCurrMtf ),
105 "DrawShape::forceScrollTextMetaFile(): Could "
106 "not extract scroll anim rectangles from mtf" );
108 // take the larger one of the two rectangles (that
109 // should be the bound rect of the retrieved
110 // metafile)
111 if( aScrollRect.isInside( aPaintRect ) )
112 maBounds = aScrollRect;
113 else
114 maBounds = aPaintRect;
116 return mpCurrMtf;
119 void DrawShape::updateStateIds() const
121 // Update the states, we've just redrawn or created a new
122 // attribute layer.
123 if( mpAttributeLayer )
125 mnAttributeTransformationState = mpAttributeLayer->getTransformationState();
126 mnAttributeClipState = mpAttributeLayer->getClipState();
127 mnAttributeAlphaState = mpAttributeLayer->getAlphaState();
128 mnAttributePositionState = mpAttributeLayer->getPositionState();
129 mnAttributeContentState = mpAttributeLayer->getContentState();
130 mnAttributeVisibilityState = mpAttributeLayer->getVisibilityState();
134 ViewShape::RenderArgs DrawShape::getViewRenderArgs() const
136 return ViewShape::RenderArgs(
137 maBounds,
138 getUpdateArea(),
139 getBounds(),
140 getActualUnitShapeBounds(),
141 mpAttributeLayer,
142 maSubsetting.getActiveSubsets(),
143 mnPriority);
146 bool DrawShape::implRender( UpdateFlags nUpdateFlags ) const
148 SAL_INFO( "slideshow", "::presentation::internal::DrawShape::implRender()" );
149 SAL_INFO( "slideshow", "::presentation::internal::DrawShape: 0x" << std::hex << this );
151 // will perform the update now, clear update-enforcing
152 // flags
153 mbForceUpdate = false;
154 mbAttributeLayerRevoked = false;
156 ENSURE_OR_RETURN_FALSE( !maViewShapes.empty(),
157 "DrawShape::implRender(): render called on DrawShape without views" );
159 if( maBounds.isEmpty() )
161 // zero-sized shapes are effectively invisible,
162 // thus, we save us the rendering...
163 return true;
166 // redraw all view shapes, by calling their update() method
167 ViewShape::RenderArgs renderArgs( getViewRenderArgs() );
168 bool bVisible = isVisible();
169 if( ::std::count_if( maViewShapes.begin(),
170 maViewShapes.end(),
171 [this, &bVisible, &renderArgs, &nUpdateFlags]
172 ( const ViewShapeSharedPtr& pShape )
173 { return pShape->update( this->mpCurrMtf,
174 renderArgs,
175 nUpdateFlags,
176 bVisible ); } )
177 != static_cast<ViewShapeVector::difference_type>(maViewShapes.size()) )
179 // at least one of the ViewShape::update() calls did return
180 // false - update failed on at least one ViewLayer
181 return false;
184 // successfully redrawn - update state IDs to detect next changes
185 updateStateIds();
187 return true;
190 UpdateFlags DrawShape::getUpdateFlags() const
192 // default: update nothing, unless ShapeAttributeStack
193 // tells us below, or if the attribute layer was revoked
194 UpdateFlags nUpdateFlags(UpdateFlags::NONE);
196 // possibly the whole shape content changed
197 if( mbAttributeLayerRevoked )
198 nUpdateFlags = UpdateFlags::Content;
201 // determine what has to be updated
204 // do we have an attribute layer?
205 if( mpAttributeLayer )
207 // Prevent nUpdateFlags to be modified when the shape is not
208 // visible, except when it just was hidden.
209 if (mpAttributeLayer->getVisibility()
210 || mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
212 if (mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
214 // Change of the visibility state is mapped to
215 // content change because when the visibility
216 // changes then usually a sprite is shown or hidden
217 // and the background under has to be painted once.
218 nUpdateFlags |= UpdateFlags::Content;
221 // TODO(P1): This can be done without conditional branching.
222 // See HAKMEM.
223 if( mpAttributeLayer->getPositionState() != mnAttributePositionState )
225 nUpdateFlags |= UpdateFlags::Position;
227 if( mpAttributeLayer->getAlphaState() != mnAttributeAlphaState )
229 nUpdateFlags |= UpdateFlags::Alpha;
231 if( mpAttributeLayer->getClipState() != mnAttributeClipState )
233 nUpdateFlags |= UpdateFlags::Clip;
235 if( mpAttributeLayer->getTransformationState() != mnAttributeTransformationState )
237 nUpdateFlags |= UpdateFlags::Transformation;
239 if( mpAttributeLayer->getContentState() != mnAttributeContentState )
241 nUpdateFlags |= UpdateFlags::Content;
246 return nUpdateFlags;
249 ::basegfx::B2DRectangle DrawShape::getActualUnitShapeBounds() const
251 ENSURE_OR_THROW( !maViewShapes.empty(),
252 "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
254 const VectorOfDocTreeNodes& rSubsets(
255 maSubsetting.getActiveSubsets() );
257 const ::basegfx::B2DRectangle aDefaultBounds( 0.0,0.0,1.0,1.0 );
259 // perform the cheapest check first
260 if( rSubsets.empty() )
262 // if subset contains the whole shape, no need to call
263 // the somewhat expensive bound calculation, since as
264 // long as the subset is empty, this branch will be
265 // taken.
266 return aDefaultBounds;
268 else
270 OSL_ENSURE( rSubsets.size() != 1 ||
271 !rSubsets.front().isEmpty(),
272 "DrawShape::getActualUnitShapeBounds() expects a "
273 "_non-empty_ subset vector for a subsetted shape!" );
275 // are the cached bounds still valid?
276 if( !maCurrentShapeUnitBounds )
278 // no, (re)generate them
279 // =====================
281 // setup cached values to defaults (might fail to
282 // retrieve true bounds below)
283 maCurrentShapeUnitBounds.reset( aDefaultBounds );
285 // TODO(P2): the subset of the master shape (that from
286 // which the subsets are subtracted) changes
287 // relatively often (every time a subset shape is
288 // added or removed). Maybe we should exclude it here,
289 // always assuming full bounds?
291 ::cppcanvas::CanvasSharedPtr pDestinationCanvas(
292 maViewShapes.front()->getViewLayer()->getCanvas() );
294 // TODO(Q2): Although this _is_ currently
295 // view-agnostic, it might not stay like
296 // that. Maybe this method should again be moved
297 // to the ViewShape
298 ::cppcanvas::RendererSharedPtr pRenderer(
299 maViewShapes.front()->getRenderer(
300 pDestinationCanvas, mpCurrMtf, mpAttributeLayer ) );
302 // If we cannot not prefetch, be defensive and assume
303 // full shape size
304 if( pRenderer )
306 // temporarily, switch total transformation to identity
307 // (need the bounds in the [0,1]x[0,1] unit coordinate
308 // system.
309 ::basegfx::B2DHomMatrix aEmptyTransformation;
311 ::basegfx::B2DHomMatrix aOldTransform( pDestinationCanvas->getTransformation() );
312 pDestinationCanvas->setTransformation( aEmptyTransformation );
313 pRenderer->setTransformation( aEmptyTransformation );
315 // restore old transformation when leaving the scope
316 const ::comphelper::ScopeGuard aGuard(
317 [&pDestinationCanvas, &aOldTransform]()
318 { return pDestinationCanvas->setTransformation( aOldTransform ); } );
321 // retrieve bounds for subset of whole metafile
324 ::basegfx::B2DRange aTotalBounds;
326 // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
327 // is overloaded.
328 for( const auto& rDocTreeNode : rSubsets )
329 aTotalBounds.expand( pRenderer->getSubsetArea(
330 rDocTreeNode.getStartIndex(),
331 rDocTreeNode.getEndIndex() ) );
333 OSL_ENSURE( aTotalBounds.getMinX() >= -0.1 &&
334 aTotalBounds.getMinY() >= -0.1 &&
335 aTotalBounds.getMaxX() <= 1.1 &&
336 aTotalBounds.getMaxY() <= 1.1,
337 "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
339 // really make sure no shape appears larger than its
340 // original bounds (there _are_ some pathologic cases,
341 // especially when imported from PPT, that have
342 // e.g. obscenely large polygon bounds)
343 aTotalBounds.intersect(
344 ::basegfx::B2DRange( 0.0, 0.0,
345 1.0, 1.0 ));
347 maCurrentShapeUnitBounds.reset( aTotalBounds );
351 return *maCurrentShapeUnitBounds;
355 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
356 const uno::Reference< drawing::XDrawPage >& xContainingPage,
357 double nPrio,
358 bool bForeignSource,
359 const SlideShowContext& rContext ) :
360 mxShape( xShape ),
361 mxPage( xContainingPage ),
362 maAnimationFrames(), // empty, we don't have no intrinsic animation
363 mnCurrFrame(0),
364 mpCurrMtf(),
365 mnCurrMtfLoadFlags( bForeignSource
366 ? MTF_LOAD_FOREIGN_SOURCE : MTF_LOAD_NONE ),
367 maCurrentShapeUnitBounds(),
368 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
369 maBounds( getAPIShapeBounds( xShape ) ),
370 mpAttributeLayer(),
371 mpIntrinsicAnimationActivity(),
372 mnAttributeTransformationState(0),
373 mnAttributeClipState(0),
374 mnAttributeAlphaState(0),
375 mnAttributePositionState(0),
376 mnAttributeContentState(0),
377 mnAttributeVisibilityState(0),
378 maViewShapes(),
379 mxComponentContext( rContext.mxComponentContext ),
380 maHyperlinkIndices(),
381 maHyperlinkRegions(),
382 maSubsetting(),
383 mnIsAnimatedCount(0),
384 mnAnimationLoopCount(0),
385 mbIsVisible( true ),
386 mbForceUpdate( false ),
387 mbAttributeLayerRevoked( false ),
388 mbDrawingLayerAnim( false )
390 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
391 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
393 // check for drawing layer animations:
394 drawing::TextAnimationKind eKind = drawing::TextAnimationKind_NONE;
395 uno::Reference<beans::XPropertySet> xPropSet( mxShape,
396 uno::UNO_QUERY );
397 if( xPropSet.is() )
398 getPropertyValue( eKind, xPropSet,
399 "TextAnimationKind" );
400 mbDrawingLayerAnim = (eKind != drawing::TextAnimationKind_NONE);
402 // must NOT be called from within initializer list, uses
403 // state from mnCurrMtfLoadFlags!
404 mpCurrMtf = getMetaFile(uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY),
405 xContainingPage, mnCurrMtfLoadFlags,
406 mxComponentContext );
407 if (!mpCurrMtf)
408 mpCurrMtf.reset(new GDIMetaFile);
410 maSubsetting.reset( mpCurrMtf );
412 prepareHyperlinkIndices();
415 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
416 const uno::Reference< drawing::XDrawPage >& xContainingPage,
417 double nPrio,
418 const Graphic& rGraphic,
419 const SlideShowContext& rContext ) :
420 mxShape( xShape ),
421 mxPage( xContainingPage ),
422 maAnimationFrames(),
423 mnCurrFrame(0),
424 mpCurrMtf(),
425 mnCurrMtfLoadFlags( MTF_LOAD_NONE ),
426 maCurrentShapeUnitBounds(),
427 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
428 maBounds( getAPIShapeBounds( xShape ) ),
429 mpAttributeLayer(),
430 mpIntrinsicAnimationActivity(),
431 mnAttributeTransformationState(0),
432 mnAttributeClipState(0),
433 mnAttributeAlphaState(0),
434 mnAttributePositionState(0),
435 mnAttributeContentState(0),
436 mnAttributeVisibilityState(0),
437 maViewShapes(),
438 mxComponentContext( rContext.mxComponentContext ),
439 maHyperlinkIndices(),
440 maHyperlinkRegions(),
441 maSubsetting(),
442 mnIsAnimatedCount(0),
443 mnAnimationLoopCount(0),
444 mbIsVisible( true ),
445 mbForceUpdate( false ),
446 mbAttributeLayerRevoked( false ),
447 mbDrawingLayerAnim( false )
449 ENSURE_OR_THROW( rGraphic.IsAnimated(),
450 "DrawShape::DrawShape(): Graphic is no animation" );
452 getAnimationFromGraphic( maAnimationFrames,
453 mnAnimationLoopCount,
454 rGraphic );
456 ENSURE_OR_THROW( !maAnimationFrames.empty() &&
457 maAnimationFrames.front().mpMtf,
458 "DrawShape::DrawShape(): " );
459 mpCurrMtf = maAnimationFrames.front().mpMtf;
461 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
462 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
463 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
466 DrawShape::DrawShape( const DrawShape& rSrc,
467 const DocTreeNode& rTreeNode,
468 double nPrio ) :
469 mxShape( rSrc.mxShape ),
470 mxPage( rSrc.mxPage ),
471 maAnimationFrames(), // don't copy animations for subsets,
472 // only the current frame!
473 mnCurrFrame(0),
474 mpCurrMtf( rSrc.mpCurrMtf ),
475 mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ),
476 maCurrentShapeUnitBounds(),
477 mnPriority( nPrio ),
478 maBounds( rSrc.maBounds ),
479 mpAttributeLayer(),
480 mpIntrinsicAnimationActivity(),
481 mnAttributeTransformationState(0),
482 mnAttributeClipState(0),
483 mnAttributeAlphaState(0),
484 mnAttributePositionState(0),
485 mnAttributeContentState(0),
486 mnAttributeVisibilityState(0),
487 maViewShapes(),
488 mxComponentContext( rSrc.mxComponentContext ),
489 maHyperlinkIndices(),
490 maHyperlinkRegions(),
491 maSubsetting( rTreeNode, mpCurrMtf ),
492 mnIsAnimatedCount(0),
493 mnAnimationLoopCount(0),
494 mbIsVisible( rSrc.mbIsVisible ),
495 mbForceUpdate( false ),
496 mbAttributeLayerRevoked( false ),
497 mbDrawingLayerAnim( false )
499 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
500 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
502 // xxx todo: currently not implemented for subsetted shapes;
503 // would mean modifying set of hyperlink regions when
504 // subsetting text portions. N.B.: there's already an
505 // issue for this #i72828#
509 // Public methods
512 DrawShapeSharedPtr DrawShape::create(
513 const uno::Reference< drawing::XShape >& xShape,
514 const uno::Reference< drawing::XDrawPage >& xContainingPage,
515 double nPrio,
516 bool bForeignSource,
517 const SlideShowContext& rContext )
519 DrawShapeSharedPtr pShape( new DrawShape(xShape,
520 xContainingPage,
521 nPrio,
522 bForeignSource,
523 rContext) );
525 if( pShape->hasIntrinsicAnimation() )
527 OSL_ASSERT( pShape->maAnimationFrames.empty() );
528 if( pShape->getNumberOfTreeNodes(
529 DocTreeNode::NodeType::LogicalParagraph) > 0 )
531 pShape->mpIntrinsicAnimationActivity =
532 createDrawingLayerAnimActivity(
533 rContext,
534 pShape);
538 if( pShape->hasHyperlinks() )
539 rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape );
541 return pShape;
544 DrawShapeSharedPtr DrawShape::create(
545 const uno::Reference< drawing::XShape >& xShape,
546 const uno::Reference< drawing::XDrawPage >& xContainingPage,
547 double nPrio,
548 const Graphic& rGraphic,
549 const SlideShowContext& rContext )
551 DrawShapeSharedPtr pShape( new DrawShape(xShape,
552 xContainingPage,
553 nPrio,
554 rGraphic,
555 rContext) );
557 if( pShape->hasIntrinsicAnimation() )
559 OSL_ASSERT( !pShape->maAnimationFrames.empty() );
561 std::vector<double> aTimeout;
562 std::transform(
563 pShape->maAnimationFrames.begin(),
564 pShape->maAnimationFrames.end(),
565 std::back_insert_iterator< std::vector<double> >( aTimeout ),
566 std::mem_fn(&MtfAnimationFrame::getDuration) );
568 WakeupEventSharedPtr pWakeupEvent(
569 new WakeupEvent( rContext.mrEventQueue.getTimer(),
570 rContext.mrActivitiesQueue ) );
572 ActivitySharedPtr pActivity =
573 createIntrinsicAnimationActivity(
574 rContext,
575 pShape,
576 pWakeupEvent,
577 aTimeout,
578 pShape->mnAnimationLoopCount);
580 pWakeupEvent->setActivity( pActivity );
581 pShape->mpIntrinsicAnimationActivity = pActivity;
584 OSL_ENSURE( !pShape->hasHyperlinks(),
585 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
587 return pShape;
590 DrawShape::~DrawShape()
594 // dispose intrinsic animation activity, else, it will
595 // linger forever
596 ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() );
597 if( pActivity )
598 pActivity->dispose();
600 catch (uno::Exception &)
602 SAL_WARN( "slideshow", "" << comphelper::anyToString(cppu::getCaughtException() ) );
606 uno::Reference< drawing::XShape > DrawShape::getXShape() const
608 return mxShape;
611 void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer,
612 bool bRedrawLayer )
614 // already added?
615 if( ::std::any_of( maViewShapes.begin(),
616 maViewShapes.end(),
617 [&rNewLayer]
618 ( const ViewShapeSharedPtr& pShape )
619 { return rNewLayer == pShape->getViewLayer(); } ) )
621 // yes, nothing to do
622 return;
625 ViewShapeSharedPtr pNewShape( new ViewShape( rNewLayer ) );
627 maViewShapes.push_back( pNewShape );
629 // pass on animation state
630 if( mnIsAnimatedCount )
632 for( int i=0; i<mnIsAnimatedCount; ++i )
633 pNewShape->enterAnimationMode();
636 // render the Shape on the newly added ViewLayer
637 if( bRedrawLayer )
639 pNewShape->update( mpCurrMtf,
640 getViewRenderArgs(),
641 UpdateFlags::Force,
642 isVisible() );
646 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer )
648 const ViewShapeVector::iterator aEnd( maViewShapes.end() );
650 OSL_ENSURE( ::std::count_if(maViewShapes.begin(),
651 aEnd,
652 [&rLayer]
653 ( const ViewShapeSharedPtr& pShape )
654 { return rLayer == pShape->getViewLayer(); } ) < 2,
655 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
657 ViewShapeVector::iterator aIter;
659 if( (aIter=::std::remove_if( maViewShapes.begin(),
660 aEnd,
661 [&rLayer]
662 ( const ViewShapeSharedPtr& pShape )
663 { return rLayer == pShape->getViewLayer(); } ) ) == aEnd )
665 // view layer seemingly was not added, failed
666 return false;
669 // actually erase from container
670 maViewShapes.erase( aIter, aEnd );
672 return true;
675 void DrawShape::clearAllViewLayers()
677 maViewShapes.clear();
680 bool DrawShape::update() const
682 if( mbForceUpdate )
684 return render();
686 else
688 return implRender( getUpdateFlags() );
692 bool DrawShape::render() const
694 // force redraw. Have to also pass on the update flags,
695 // because e.g. content update (regeneration of the
696 // metafile renderer) is normally not performed. A simple
697 // UpdateFlags::Force would only paint the metafile in its
698 // old state.
699 return implRender( UpdateFlags::Force | getUpdateFlags() );
702 bool DrawShape::isContentChanged() const
704 return mbForceUpdate ||
705 getUpdateFlags() != UpdateFlags::NONE;
709 ::basegfx::B2DRectangle DrawShape::getBounds() const
711 // little optimization: for non-modified shapes, we don't
712 // create an ShapeAttributeStack, and therefore also don't
713 // have to check it.
714 return getShapePosSize( maBounds,
715 mpAttributeLayer );
718 ::basegfx::B2DRectangle DrawShape::getDomBounds() const
720 return maBounds;
723 ::basegfx::B2DRectangle DrawShape::getUpdateArea() const
725 ::basegfx::B2DRectangle aBounds;
727 // an already empty shape bound need no further
728 // treatment. In fact, any changes applied below would
729 // actually remove the special empty state, thus, don't
730 // change!
731 if( !maBounds.isEmpty() )
733 basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0);
735 if( !maViewShapes.empty() )
736 aUnitBounds = getActualUnitShapeBounds();
738 if( !aUnitBounds.isEmpty() )
740 if( mpAttributeLayer )
742 // calc actual shape area (in user coordinate
743 // space) from the transformation as given by the
744 // shape attribute layer
745 aBounds = getShapeUpdateArea( aUnitBounds,
746 getShapeTransformation( getBounds(),
747 mpAttributeLayer ),
748 mpAttributeLayer );
750 else
752 // no attribute layer, thus, the true shape bounds
753 // can be directly derived from the XShape bound
754 // attribute
755 aBounds = getShapeUpdateArea( aUnitBounds,
756 maBounds );
759 if( !maViewShapes.empty() )
761 // determine border needed for antialiasing the shape
762 ::basegfx::B2DSize aAABorder(0.0,0.0);
764 // for every view, get AA border and 'expand' aAABorder
765 // appropriately.
766 for( const auto& rViewShape : maViewShapes )
768 const ::basegfx::B2DSize rShapeBorder( rViewShape->getAntialiasingBorder() );
770 aAABorder.setX( ::std::max(
771 rShapeBorder.getX(),
772 aAABorder.getX() ) );
773 aAABorder.setY( ::std::max(
774 rShapeBorder.getY(),
775 aAABorder.getY() ) );
778 // add calculated AA border to aBounds
779 aBounds = ::basegfx::B2DRectangle( aBounds.getMinX() - aAABorder.getX(),
780 aBounds.getMinY() - aAABorder.getY(),
781 aBounds.getMaxX() + aAABorder.getX(),
782 aBounds.getMaxY() + aAABorder.getY() );
787 return aBounds;
790 bool DrawShape::isVisible() const
792 bool bIsVisible( mbIsVisible );
794 if( mpAttributeLayer )
796 // check whether visibility and alpha are not default
797 // (mpAttributeLayer->isVisibilityValid() returns true
798 // then): bVisible becomes true, if shape visibility
799 // is on and alpha is not 0.0 (fully transparent)
800 if( mpAttributeLayer->isVisibilityValid() )
801 bIsVisible = mpAttributeLayer->getVisibility();
803 // only touch bIsVisible, if the shape is still
804 // visible - if getVisibility already made us
805 // invisible, no alpha value will make us appear
806 // again.
807 if( bIsVisible && mpAttributeLayer->isAlphaValid() )
808 bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
811 return bIsVisible;
814 double DrawShape::getPriority() const
816 return mnPriority;
819 bool DrawShape::isBackgroundDetached() const
821 return mnIsAnimatedCount > 0;
824 bool DrawShape::hasIntrinsicAnimation() const
826 return (!maAnimationFrames.empty() || mbDrawingLayerAnim);
829 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame )
831 ENSURE_OR_RETURN_VOID( nCurrFrame < maAnimationFrames.size(),
832 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
834 if( mnCurrFrame != nCurrFrame )
836 mnCurrFrame = nCurrFrame;
837 mpCurrMtf = maAnimationFrames[ mnCurrFrame ].mpMtf;
838 mbForceUpdate = true;
842 // hyperlink support
843 void DrawShape::prepareHyperlinkIndices() const
845 if ( !maHyperlinkIndices.empty())
847 maHyperlinkIndices.clear();
848 maHyperlinkRegions.clear();
851 sal_Int32 nIndex = 0;
852 for ( MetaAction * pCurrAct = mpCurrMtf->FirstAction();
853 pCurrAct != nullptr; pCurrAct = mpCurrMtf->NextAction() )
855 if (pCurrAct->GetType() == MetaActionType::COMMENT) {
856 MetaCommentAction * pAct =
857 static_cast<MetaCommentAction *>(pCurrAct);
858 // skip comment if not a special XTEXT comment
859 if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
860 // e.g. date field doesn't have data!
861 // currently assuming that only url field, this is
862 // somehow fragile! xxx todo if possible
863 pAct->GetData() != nullptr &&
864 pAct->GetDataSize() > 0)
866 if (!maHyperlinkIndices.empty() &&
867 maHyperlinkIndices.back().second == -1) {
868 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
869 maHyperlinkIndices.pop_back();
870 maHyperlinkRegions.pop_back();
872 maHyperlinkIndices.emplace_back( nIndex + 1,
873 -1 /* to be filled below */ );
874 maHyperlinkRegions.emplace_back(
875 basegfx::B2DRectangle(),
876 OUString(
877 reinterpret_cast<sal_Unicode const*>(
878 pAct->GetData()),
879 pAct->GetDataSize() / sizeof(sal_Unicode) )
882 else if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
883 // pending end is expected:
884 !maHyperlinkIndices.empty() &&
885 maHyperlinkIndices.back().second == -1)
887 maHyperlinkIndices.back().second = nIndex;
889 ++nIndex;
891 else
892 nIndex += getNextActionOffset(pCurrAct);
894 if (!maHyperlinkIndices.empty() &&
895 maHyperlinkIndices.back().second == -1) {
896 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
897 maHyperlinkIndices.pop_back();
898 maHyperlinkRegions.pop_back();
900 OSL_ASSERT( maHyperlinkIndices.size() == maHyperlinkRegions.size());
903 bool DrawShape::hasHyperlinks() const
905 return ! maHyperlinkRegions.empty();
908 HyperlinkArea::HyperlinkRegions DrawShape::getHyperlinkRegions() const
910 OSL_ASSERT( !maViewShapes.empty() );
912 if( !isVisible() )
913 return HyperlinkArea::HyperlinkRegions();
915 // late init, determine regions:
916 if( !maHyperlinkRegions.empty() &&
917 !maViewShapes.empty() &&
918 // region already inited?
919 maHyperlinkRegions.front().first.getWidth() == 0 &&
920 maHyperlinkRegions.front().first.getHeight() == 0 &&
921 maHyperlinkRegions.size() == maHyperlinkIndices.size() )
923 // TODO(Q2): Although this _is_ currently
924 // view-agnostic, it might not stay like that.
925 ViewShapeSharedPtr const& pViewShape = maViewShapes.front();
926 cppcanvas::CanvasSharedPtr const pCanvas(
927 pViewShape->getViewLayer()->getCanvas() );
929 // reuse Renderer of first view shape:
930 cppcanvas::RendererSharedPtr const pRenderer(
931 pViewShape->getRenderer(
932 pCanvas, mpCurrMtf, mpAttributeLayer ) );
934 OSL_ASSERT( pRenderer );
936 if (pRenderer)
938 basegfx::B2DHomMatrix const aOldTransform(
939 pCanvas->getTransformation() );
940 basegfx::B2DHomMatrix aTransform;
941 pCanvas->setTransformation( aTransform /* empty */ );
944 ::cppcanvas::Canvas* pTmpCanvas = pCanvas.get();
945 comphelper::ScopeGuard const resetOldTransformation(
946 [&aOldTransform, &pTmpCanvas]()
947 { return pTmpCanvas->setTransformation( aOldTransform ); } );
949 aTransform.scale( maBounds.getWidth(),
950 maBounds.getHeight() );
951 pRenderer->setTransformation( aTransform );
952 pRenderer->setClip();
954 for( std::size_t pos = maHyperlinkRegions.size(); pos--; )
956 // get region:
957 HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos];
958 basegfx::B2DRectangle const region(
959 pRenderer->getSubsetArea( rIndices.first,
960 rIndices.second ));
961 maHyperlinkRegions[pos].first = region;
966 // shift shape-relative hyperlink regions to
967 // slide-absolute position
969 HyperlinkRegions aTranslatedRegions;
971 // increase capacity to same size as the container for
972 // shape-relative hyperlink regions to avoid reallocation
973 aTranslatedRegions.reserve( maHyperlinkRegions.size() );
974 const basegfx::B2DPoint& rOffset(getBounds().getMinimum());
975 for( const auto& cp : maHyperlinkRegions )
977 basegfx::B2DRange const& relRegion( cp.first );
978 aTranslatedRegions.emplace_back(
979 basegfx::B2DRange(
980 relRegion.getMinimum() + rOffset,
981 relRegion.getMaximum() + rOffset),
982 cp.second );
985 return aTranslatedRegions;
988 double DrawShape::getHyperlinkPriority() const
990 return getPriority();
994 // AnimatableShape methods
997 void DrawShape::enterAnimationMode()
999 OSL_ENSURE( !maViewShapes.empty(),
1000 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1002 if( mnIsAnimatedCount == 0 )
1004 // notify all ViewShapes, by calling their enterAnimationMode method.
1005 // We're now entering animation mode
1006 for( const auto& rViewShape : maViewShapes )
1007 rViewShape->enterAnimationMode();
1010 ++mnIsAnimatedCount;
1013 void DrawShape::leaveAnimationMode()
1015 OSL_ENSURE( !maViewShapes.empty(),
1016 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1018 --mnIsAnimatedCount;
1020 if( mnIsAnimatedCount == 0 )
1022 // notify all ViewShapes, by calling their leaveAnimationMode method.
1023 // we're now leaving animation mode
1024 for( const auto& rViewShape : maViewShapes )
1025 rViewShape->leaveAnimationMode();
1030 // AttributableShape methods
1033 ShapeAttributeLayerSharedPtr DrawShape::createAttributeLayer()
1035 // create new layer, with last as its new child
1036 mpAttributeLayer.reset( new ShapeAttributeLayer( mpAttributeLayer ) );
1038 // Update the local state ids to reflect those of the new layer.
1039 updateStateIds();
1041 return mpAttributeLayer;
1044 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr& rLayer )
1046 if( !mpAttributeLayer )
1047 return false; // no layers
1049 if( mpAttributeLayer == rLayer )
1051 // it's the toplevel layer
1052 mpAttributeLayer = mpAttributeLayer->getChildLayer();
1054 // force content redraw, all state variables have
1055 // possibly changed
1056 mbAttributeLayerRevoked = true;
1058 return true;
1060 else
1062 // pass on to the layer, to try its children
1063 return mpAttributeLayer->revokeChildLayer( rLayer );
1067 ShapeAttributeLayerSharedPtr DrawShape::getTopmostAttributeLayer() const
1069 return mpAttributeLayer;
1072 void DrawShape::setVisibility( bool bVisible )
1074 if( mbIsVisible != bVisible )
1076 mbIsVisible = bVisible;
1077 mbForceUpdate = true;
1081 const DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier() const
1083 return *this;
1086 DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier()
1088 return *this;
1091 DocTreeNode DrawShape::getSubsetNode() const
1093 // forward to delegate
1094 return maSubsetting.getSubsetNode();
1097 AttributableShapeSharedPtr DrawShape::getSubset( const DocTreeNode& rTreeNode ) const
1099 // forward to delegate
1100 return maSubsetting.getSubsetShape( rTreeNode );
1103 bool DrawShape::createSubset( AttributableShapeSharedPtr& o_rSubset,
1104 const DocTreeNode& rTreeNode )
1106 // subset shape already created for this DocTreeNode?
1107 AttributableShapeSharedPtr pSubset( maSubsetting.getSubsetShape( rTreeNode ) );
1109 // when true, this method has created a new subset
1110 // DrawShape
1111 bool bNewlyCreated( false );
1113 if( pSubset )
1115 o_rSubset = pSubset;
1117 // reusing existing subset
1119 else
1121 // not yet created, init entry
1122 o_rSubset.reset( new DrawShape( *this,
1123 rTreeNode,
1124 // TODO(Q3): That's a
1125 // hack. We assume
1126 // that start and end
1127 // index will always
1128 // be less than 65535
1129 mnPriority +
1130 rTreeNode.getStartIndex()/double(SAL_MAX_INT16) ));
1132 bNewlyCreated = true; // subset newly created
1135 // always register shape at DrawShapeSubsetting, to keep
1136 // refcount up-to-date
1137 maSubsetting.addSubsetShape( o_rSubset );
1139 // flush bounds cache
1140 maCurrentShapeUnitBounds.reset();
1142 return bNewlyCreated;
1145 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr& rShape )
1147 // flush bounds cache
1148 maCurrentShapeUnitBounds.reset();
1150 // forward to delegate
1151 if( maSubsetting.revokeSubsetShape( rShape ) )
1153 // force redraw, our content has possibly changed (as
1154 // one of the subsets now display within our shape
1155 // again).
1156 mbForceUpdate = true;
1158 // #i47428# TEMP FIX: synchronize visibility of subset
1159 // with parent.
1161 // TODO(F3): Remove here, and implement
1162 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1163 // additional level of indirection: create a
1164 // persistent subset, containing all text/only the
1165 // background respectively. From _that_ object,
1166 // generate the temporary character subset shapes.
1167 const ShapeAttributeLayerSharedPtr& rAttrLayer(
1168 rShape->getTopmostAttributeLayer() );
1169 if( rAttrLayer &&
1170 rAttrLayer->isVisibilityValid() &&
1171 rAttrLayer->getVisibility() != isVisible() )
1173 const bool bVisibility( rAttrLayer->getVisibility() );
1175 // visibilities differ - adjust ours, then
1176 if( mpAttributeLayer )
1177 mpAttributeLayer->setVisibility( bVisibility );
1178 else
1179 mbIsVisible = bVisibility;
1182 // END TEMP FIX
1184 return true;
1187 return false;
1190 sal_Int32 DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1192 return maSubsetting.getNumberOfTreeNodes( eNodeType );
1195 DocTreeNode DrawShape::getTreeNode( sal_Int32 nNodeIndex,
1196 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1198 if ( hasHyperlinks())
1200 prepareHyperlinkIndices();
1203 return maSubsetting.getTreeNode( nNodeIndex, eNodeType );
1206 sal_Int32 DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode& rParentNode,
1207 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1209 return maSubsetting.getNumberOfSubsetTreeNodes( rParentNode, eNodeType );
1212 DocTreeNode DrawShape::getSubsetTreeNode( const DocTreeNode& rParentNode,
1213 sal_Int32 nNodeIndex,
1214 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1216 return maSubsetting.getSubsetTreeNode( rParentNode, nNodeIndex, eNodeType );
1221 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */