build fix
[LibreOffice.git] / slideshow / source / engine / shapes / drawshape.cxx
blob877049b0b5aec59eecefbc3742a1440c21971ca3
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/frame/XModel.hpp>
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 mpCurrMtf.reset( new GDIMetaFile );
86 mnCurrMtfLoadFlags |= MTF_LOAD_SCROLL_TEXT_MTF;
87 getMetaFile(
88 uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY),
89 mxPage, *mpCurrMtf, mnCurrMtfLoadFlags,
90 mxComponentContext );
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( int 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 int DrawShape::getUpdateFlags() const
192 // default: update nothing, unless ShapeAttributeStack
193 // tells us below, or if the attribute layer was revoked
194 int nUpdateFlags(ViewShape::NONE);
196 // possibly the whole shape content changed
197 if( mbAttributeLayerRevoked )
198 nUpdateFlags = ViewShape::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 |= ViewShape::CONTENT;
221 // TODO(P1): This can be done without conditional branching.
222 // See HAKMEM.
223 if( mpAttributeLayer->getPositionState() != mnAttributePositionState )
225 nUpdateFlags |= ViewShape::POSITION;
227 if( mpAttributeLayer->getAlphaState() != mnAttributeAlphaState )
229 nUpdateFlags |= ViewShape::ALPHA;
231 if( mpAttributeLayer->getClipState() != mnAttributeClipState )
233 nUpdateFlags |= ViewShape::CLIP;
235 if( mpAttributeLayer->getTransformationState() != mnAttributeTransformationState )
237 nUpdateFlags |= ViewShape::TRANSFORMATION;
239 if( mpAttributeLayer->getContentState() != mnAttributeContentState )
241 nUpdateFlags |= ViewShape::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 meCycleMode(CYCLE_LOOP),
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.reset( new GDIMetaFile );
406 getMetaFile(
407 uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY),
408 xContainingPage, *mpCurrMtf, mnCurrMtfLoadFlags,
409 mxComponentContext );
410 ENSURE_OR_THROW( mpCurrMtf,
411 "DrawShape::DrawShape(): Invalid metafile" );
412 maSubsetting.reset( mpCurrMtf );
414 prepareHyperlinkIndices();
417 DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape,
418 const uno::Reference< drawing::XDrawPage >& xContainingPage,
419 double nPrio,
420 const Graphic& rGraphic,
421 const SlideShowContext& rContext ) :
422 mxShape( xShape ),
423 mxPage( xContainingPage ),
424 maAnimationFrames(),
425 mnCurrFrame(0),
426 mpCurrMtf(),
427 mnCurrMtfLoadFlags( MTF_LOAD_NONE ),
428 maCurrentShapeUnitBounds(),
429 mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
430 maBounds( getAPIShapeBounds( xShape ) ),
431 mpAttributeLayer(),
432 mpIntrinsicAnimationActivity(),
433 mnAttributeTransformationState(0),
434 mnAttributeClipState(0),
435 mnAttributeAlphaState(0),
436 mnAttributePositionState(0),
437 mnAttributeContentState(0),
438 mnAttributeVisibilityState(0),
439 maViewShapes(),
440 mxComponentContext( rContext.mxComponentContext ),
441 maHyperlinkIndices(),
442 maHyperlinkRegions(),
443 maSubsetting(),
444 mnIsAnimatedCount(0),
445 mnAnimationLoopCount(0),
446 meCycleMode(CYCLE_LOOP),
447 mbIsVisible( true ),
448 mbForceUpdate( false ),
449 mbAttributeLayerRevoked( false ),
450 mbDrawingLayerAnim( false )
452 ENSURE_OR_THROW( rGraphic.IsAnimated(),
453 "DrawShape::DrawShape(): Graphic is no animation" );
455 getAnimationFromGraphic( maAnimationFrames,
456 mnAnimationLoopCount,
457 meCycleMode,
458 rGraphic );
460 ENSURE_OR_THROW( !maAnimationFrames.empty() &&
461 maAnimationFrames.front().mpMtf,
462 "DrawShape::DrawShape(): " );
463 mpCurrMtf = maAnimationFrames.front().mpMtf;
465 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
466 ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
467 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
470 DrawShape::DrawShape( const DrawShape& rSrc,
471 const DocTreeNode& rTreeNode,
472 double nPrio ) :
473 mxShape( rSrc.mxShape ),
474 mxPage( rSrc.mxPage ),
475 maAnimationFrames(), // don't copy animations for subsets,
476 // only the current frame!
477 mnCurrFrame(0),
478 mpCurrMtf( rSrc.mpCurrMtf ),
479 mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ),
480 maCurrentShapeUnitBounds(),
481 mnPriority( nPrio ),
482 maBounds( rSrc.maBounds ),
483 mpAttributeLayer(),
484 mpIntrinsicAnimationActivity(),
485 mnAttributeTransformationState(0),
486 mnAttributeClipState(0),
487 mnAttributeAlphaState(0),
488 mnAttributePositionState(0),
489 mnAttributeContentState(0),
490 mnAttributeVisibilityState(0),
491 maViewShapes(),
492 mxComponentContext( rSrc.mxComponentContext ),
493 maHyperlinkIndices(),
494 maHyperlinkRegions(),
495 maSubsetting( rTreeNode, mpCurrMtf ),
496 mnIsAnimatedCount(0),
497 mnAnimationLoopCount(0),
498 meCycleMode(CYCLE_LOOP),
499 mbIsVisible( rSrc.mbIsVisible ),
500 mbForceUpdate( false ),
501 mbAttributeLayerRevoked( false ),
502 mbDrawingLayerAnim( false )
504 ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
505 ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
507 // xxx todo: currently not implemented for subsetted shapes;
508 // would mean modifying set of hyperlink regions when
509 // subsetting text portions. N.B.: there's already an
510 // issue for this #i72828#
514 // Public methods
517 DrawShapeSharedPtr DrawShape::create(
518 const uno::Reference< drawing::XShape >& xShape,
519 const uno::Reference< drawing::XDrawPage >& xContainingPage,
520 double nPrio,
521 bool bForeignSource,
522 const SlideShowContext& rContext )
524 DrawShapeSharedPtr pShape( new DrawShape(xShape,
525 xContainingPage,
526 nPrio,
527 bForeignSource,
528 rContext) );
530 if( pShape->hasIntrinsicAnimation() )
532 OSL_ASSERT( pShape->maAnimationFrames.empty() );
533 if( pShape->getNumberOfTreeNodes(
534 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH) > 0 )
536 pShape->mpIntrinsicAnimationActivity =
537 createDrawingLayerAnimActivity(
538 rContext,
539 pShape);
543 if( pShape->hasHyperlinks() )
544 rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape );
546 return pShape;
549 DrawShapeSharedPtr DrawShape::create(
550 const uno::Reference< drawing::XShape >& xShape,
551 const uno::Reference< drawing::XDrawPage >& xContainingPage,
552 double nPrio,
553 const Graphic& rGraphic,
554 const SlideShowContext& rContext )
556 DrawShapeSharedPtr pShape( new DrawShape(xShape,
557 xContainingPage,
558 nPrio,
559 rGraphic,
560 rContext) );
562 if( pShape->hasIntrinsicAnimation() )
564 OSL_ASSERT( !pShape->maAnimationFrames.empty() );
566 std::vector<double> aTimeout;
567 std::transform(
568 pShape->maAnimationFrames.begin(),
569 pShape->maAnimationFrames.end(),
570 std::back_insert_iterator< std::vector<double> >( aTimeout ),
571 std::mem_fn(&MtfAnimationFrame::getDuration) );
573 WakeupEventSharedPtr pWakeupEvent(
574 new WakeupEvent( rContext.mrEventQueue.getTimer(),
575 rContext.mrActivitiesQueue ) );
577 ActivitySharedPtr pActivity =
578 createIntrinsicAnimationActivity(
579 rContext,
580 pShape,
581 pWakeupEvent,
582 aTimeout,
583 pShape->mnAnimationLoopCount,
584 pShape->meCycleMode);
586 pWakeupEvent->setActivity( pActivity );
587 pShape->mpIntrinsicAnimationActivity = pActivity;
590 OSL_ENSURE( !pShape->hasHyperlinks(),
591 "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
593 return pShape;
596 DrawShape::~DrawShape()
600 // dispose intrinsic animation activity, else, it will
601 // linger forever
602 ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() );
603 if( pActivity )
604 pActivity->dispose();
606 catch (uno::Exception &)
608 SAL_WARN( "slideshow", "" << comphelper::anyToString(cppu::getCaughtException() ) );
612 uno::Reference< drawing::XShape > DrawShape::getXShape() const
614 return mxShape;
617 void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer,
618 bool bRedrawLayer )
620 // already added?
621 if( ::std::any_of( maViewShapes.begin(),
622 maViewShapes.end(),
623 [&rNewLayer]
624 ( const ViewShapeSharedPtr& pShape )
625 { return rNewLayer == pShape->getViewLayer(); } ) )
627 // yes, nothing to do
628 return;
631 ViewShapeSharedPtr pNewShape( new ViewShape( rNewLayer ) );
633 maViewShapes.push_back( pNewShape );
635 // pass on animation state
636 if( mnIsAnimatedCount )
638 for( int i=0; i<mnIsAnimatedCount; ++i )
639 pNewShape->enterAnimationMode();
642 // render the Shape on the newly added ViewLayer
643 if( bRedrawLayer )
645 pNewShape->update( mpCurrMtf,
646 getViewRenderArgs(),
647 ViewShape::FORCE,
648 isVisible() );
652 bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer )
654 const ViewShapeVector::iterator aEnd( maViewShapes.end() );
656 OSL_ENSURE( ::std::count_if(maViewShapes.begin(),
657 aEnd,
658 [&rLayer]
659 ( const ViewShapeSharedPtr& pShape )
660 { return rLayer == pShape->getViewLayer(); } ) < 2,
661 "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
663 ViewShapeVector::iterator aIter;
665 if( (aIter=::std::remove_if( maViewShapes.begin(),
666 aEnd,
667 [&rLayer]
668 ( const ViewShapeSharedPtr& pShape )
669 { return rLayer == pShape->getViewLayer(); } ) ) == aEnd )
671 // view layer seemingly was not added, failed
672 return false;
675 // actually erase from container
676 maViewShapes.erase( aIter, aEnd );
678 return true;
681 void DrawShape::clearAllViewLayers()
683 maViewShapes.clear();
686 bool DrawShape::update() const
688 if( mbForceUpdate )
690 return render();
692 else
694 return implRender( getUpdateFlags() );
698 bool DrawShape::render() const
700 // force redraw. Have to also pass on the update flags,
701 // because e.g. content update (regeneration of the
702 // metafile renderer) is normally not performed. A simple
703 // ViewShape::FORCE would only paint the metafile in its
704 // old state.
705 return implRender( ViewShape::FORCE | getUpdateFlags() );
708 bool DrawShape::isContentChanged() const
710 return mbForceUpdate ||
711 getUpdateFlags() != ViewShape::NONE;
715 ::basegfx::B2DRectangle DrawShape::getBounds() const
717 // little optimization: for non-modified shapes, we don't
718 // create an ShapeAttributeStack, and therefore also don't
719 // have to check it.
720 return getShapePosSize( maBounds,
721 mpAttributeLayer );
724 ::basegfx::B2DRectangle DrawShape::getDomBounds() const
726 return maBounds;
729 ::basegfx::B2DRectangle DrawShape::getUpdateArea() const
731 ::basegfx::B2DRectangle aBounds;
733 // an already empty shape bound need no further
734 // treatment. In fact, any changes applied below would
735 // actually remove the special empty state, thus, don't
736 // change!
737 if( !maBounds.isEmpty() )
739 basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0);
741 if( !maViewShapes.empty() )
742 aUnitBounds = getActualUnitShapeBounds();
744 if( !aUnitBounds.isEmpty() )
746 if( mpAttributeLayer )
748 // calc actual shape area (in user coordinate
749 // space) from the transformation as given by the
750 // shape attribute layer
751 aBounds = getShapeUpdateArea( aUnitBounds,
752 getShapeTransformation( getBounds(),
753 mpAttributeLayer ),
754 mpAttributeLayer );
756 else
758 // no attribute layer, thus, the true shape bounds
759 // can be directly derived from the XShape bound
760 // attribute
761 aBounds = getShapeUpdateArea( aUnitBounds,
762 maBounds );
765 if( !maViewShapes.empty() )
767 // determine border needed for antialiasing the shape
768 ::basegfx::B2DSize aAABorder(0.0,0.0);
770 // for every view, get AA border and 'expand' aAABorder
771 // appropriately.
772 for( const auto& rViewShape : maViewShapes )
774 const ::basegfx::B2DSize rShapeBorder( rViewShape->getAntialiasingBorder() );
776 aAABorder.setX( ::std::max(
777 rShapeBorder.getX(),
778 aAABorder.getX() ) );
779 aAABorder.setY( ::std::max(
780 rShapeBorder.getY(),
781 aAABorder.getY() ) );
784 // add calculated AA border to aBounds
785 aBounds = ::basegfx::B2DRectangle( aBounds.getMinX() - aAABorder.getX(),
786 aBounds.getMinY() - aAABorder.getY(),
787 aBounds.getMaxX() + aAABorder.getX(),
788 aBounds.getMaxY() + aAABorder.getY() );
793 return aBounds;
796 bool DrawShape::isVisible() const
798 bool bIsVisible( mbIsVisible );
800 if( mpAttributeLayer )
802 // check whether visibility and alpha are not default
803 // (mpAttributeLayer->isVisibilityValid() returns true
804 // then): bVisible becomes true, if shape visibility
805 // is on and alpha is not 0.0 (fully transparent)
806 if( mpAttributeLayer->isVisibilityValid() )
807 bIsVisible = mpAttributeLayer->getVisibility();
809 // only touch bIsVisible, if the shape is still
810 // visible - if getVisibility already made us
811 // invisible, no alpha value will make us appear
812 // again.
813 if( bIsVisible && mpAttributeLayer->isAlphaValid() )
814 bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
817 return bIsVisible;
820 double DrawShape::getPriority() const
822 return mnPriority;
825 bool DrawShape::isBackgroundDetached() const
827 return mnIsAnimatedCount > 0;
830 bool DrawShape::hasIntrinsicAnimation() const
832 return (!maAnimationFrames.empty() || mbDrawingLayerAnim);
835 void DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame )
837 ENSURE_OR_RETURN_VOID( nCurrFrame < maAnimationFrames.size(),
838 "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
840 if( mnCurrFrame != nCurrFrame )
842 mnCurrFrame = nCurrFrame;
843 mpCurrMtf = maAnimationFrames[ mnCurrFrame ].mpMtf;
844 mbForceUpdate = true;
848 // hyperlink support
849 void DrawShape::prepareHyperlinkIndices() const
851 if ( !maHyperlinkIndices.empty())
853 maHyperlinkIndices.clear();
854 maHyperlinkRegions.clear();
857 sal_Int32 nIndex = 0;
858 for ( MetaAction * pCurrAct = mpCurrMtf->FirstAction();
859 pCurrAct != nullptr; pCurrAct = mpCurrMtf->NextAction() )
861 if (pCurrAct->GetType() == MetaActionType::COMMENT) {
862 MetaCommentAction * pAct =
863 static_cast<MetaCommentAction *>(pCurrAct);
864 // skip comment if not a special XTEXT comment
865 if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_BEGIN") &&
866 // e.g. date field doesn't have data!
867 // currently assuming that only url field, this is
868 // somehow fragile! xxx todo if possible
869 pAct->GetData() != nullptr &&
870 pAct->GetDataSize() > 0)
872 if (!maHyperlinkIndices.empty() &&
873 maHyperlinkIndices.back().second == -1) {
874 SAL_WARN( "slideshow", "### pending FIELD_SEQ_END!" );
875 maHyperlinkIndices.pop_back();
876 maHyperlinkRegions.pop_back();
878 maHyperlinkIndices.push_back(
879 HyperlinkIndexPair( nIndex + 1,
880 -1 /* to be filled below */ ) );
881 maHyperlinkRegions.push_back(
882 HyperlinkRegion(
883 basegfx::B2DRectangle(),
884 OUString(
885 reinterpret_cast<sal_Unicode const*>(
886 pAct->GetData()),
887 pAct->GetDataSize() / sizeof(sal_Unicode) )
888 ) );
890 else if (pAct->GetComment().equalsIgnoreAsciiCase("FIELD_SEQ_END") &&
891 // pending end is expected:
892 !maHyperlinkIndices.empty() &&
893 maHyperlinkIndices.back().second == -1)
895 maHyperlinkIndices.back().second = nIndex;
897 ++nIndex;
899 else
900 nIndex += getNextActionOffset(pCurrAct);
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 OSL_ASSERT( maHyperlinkIndices.size() == maHyperlinkRegions.size());
911 bool DrawShape::hasHyperlinks() const
913 return ! maHyperlinkRegions.empty();
916 HyperlinkArea::HyperlinkRegions DrawShape::getHyperlinkRegions() const
918 OSL_ASSERT( !maViewShapes.empty() );
920 if( !isVisible() )
921 return HyperlinkArea::HyperlinkRegions();
923 // late init, determine regions:
924 if( !maHyperlinkRegions.empty() &&
925 !maViewShapes.empty() &&
926 // region already inited?
927 maHyperlinkRegions.front().first.getWidth() == 0 &&
928 maHyperlinkRegions.front().first.getHeight() == 0 &&
929 maHyperlinkRegions.size() == maHyperlinkIndices.size() )
931 // TODO(Q2): Although this _is_ currently
932 // view-agnostic, it might not stay like that.
933 ViewShapeSharedPtr const& pViewShape = maViewShapes.front();
934 cppcanvas::CanvasSharedPtr const pCanvas(
935 pViewShape->getViewLayer()->getCanvas() );
937 // reuse Renderer of first view shape:
938 cppcanvas::RendererSharedPtr const pRenderer(
939 pViewShape->getRenderer(
940 pCanvas, mpCurrMtf, mpAttributeLayer ) );
942 OSL_ASSERT( pRenderer );
944 if (pRenderer)
946 basegfx::B2DHomMatrix const aOldTransform(
947 pCanvas->getTransformation() );
948 basegfx::B2DHomMatrix aTransform;
949 pCanvas->setTransformation( aTransform /* empty */ );
952 ::cppcanvas::Canvas* pTmpCanvas = pCanvas.get();
953 comphelper::ScopeGuard const resetOldTransformation(
954 [&aOldTransform, &pTmpCanvas]()
955 { return pTmpCanvas->setTransformation( aOldTransform ); } );
957 aTransform.scale( maBounds.getWidth(),
958 maBounds.getHeight() );
959 pRenderer->setTransformation( aTransform );
960 pRenderer->setClip();
962 for( std::size_t pos = maHyperlinkRegions.size(); pos--; )
964 // get region:
965 HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos];
966 basegfx::B2DRectangle const region(
967 pRenderer->getSubsetArea( rIndices.first,
968 rIndices.second ));
969 maHyperlinkRegions[pos].first = region;
974 // shift shape-relative hyperlink regions to
975 // slide-absolute position
977 HyperlinkRegions aTranslatedRegions;
979 // increase capacity to same size as the container for
980 // shape-relative hyperlink regions to avoid reallocation
981 aTranslatedRegions.reserve( maHyperlinkRegions.size() );
982 const basegfx::B2DPoint& rOffset(getBounds().getMinimum());
983 for( const auto& cp : maHyperlinkRegions )
985 basegfx::B2DRange const& relRegion( cp.first );
986 aTranslatedRegions.push_back(
987 std::make_pair(
988 basegfx::B2DRange(
989 relRegion.getMinimum() + rOffset,
990 relRegion.getMaximum() + rOffset),
991 cp.second) );
994 return aTranslatedRegions;
997 double DrawShape::getHyperlinkPriority() const
999 return getPriority();
1003 // AnimatableShape methods
1006 void DrawShape::enterAnimationMode()
1008 OSL_ENSURE( !maViewShapes.empty(),
1009 "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1011 if( mnIsAnimatedCount == 0 )
1013 // notify all ViewShapes, by calling their enterAnimationMode method.
1014 // We're now entering animation mode
1015 for( const auto& rViewShape : maViewShapes )
1016 rViewShape->enterAnimationMode();
1019 ++mnIsAnimatedCount;
1022 void DrawShape::leaveAnimationMode()
1024 OSL_ENSURE( !maViewShapes.empty(),
1025 "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1027 --mnIsAnimatedCount;
1029 if( mnIsAnimatedCount == 0 )
1031 // notify all ViewShapes, by calling their leaveAnimationMode method.
1032 // we're now leaving animation mode
1033 for( const auto& rViewShape : maViewShapes )
1034 rViewShape->leaveAnimationMode();
1039 // AttributableShape methods
1042 ShapeAttributeLayerSharedPtr DrawShape::createAttributeLayer()
1044 // create new layer, with last as its new child
1045 mpAttributeLayer.reset( new ShapeAttributeLayer( mpAttributeLayer ) );
1047 // Update the local state ids to reflect those of the new layer.
1048 updateStateIds();
1050 return mpAttributeLayer;
1053 bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr& rLayer )
1055 if( !mpAttributeLayer )
1056 return false; // no layers
1058 if( mpAttributeLayer == rLayer )
1060 // it's the toplevel layer
1061 mpAttributeLayer = mpAttributeLayer->getChildLayer();
1063 // force content redraw, all state variables have
1064 // possibly changed
1065 mbAttributeLayerRevoked = true;
1067 return true;
1069 else
1071 // pass on to the layer, to try its children
1072 return mpAttributeLayer->revokeChildLayer( rLayer );
1076 ShapeAttributeLayerSharedPtr DrawShape::getTopmostAttributeLayer() const
1078 return mpAttributeLayer;
1081 void DrawShape::setVisibility( bool bVisible )
1083 if( mbIsVisible != bVisible )
1085 mbIsVisible = bVisible;
1086 mbForceUpdate = true;
1090 const DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier() const
1092 return *this;
1095 DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier()
1097 return *this;
1100 DocTreeNode DrawShape::getSubsetNode() const
1102 // forward to delegate
1103 return maSubsetting.getSubsetNode();
1106 AttributableShapeSharedPtr DrawShape::getSubset( const DocTreeNode& rTreeNode ) const
1108 // forward to delegate
1109 return maSubsetting.getSubsetShape( rTreeNode );
1112 bool DrawShape::createSubset( AttributableShapeSharedPtr& o_rSubset,
1113 const DocTreeNode& rTreeNode )
1115 // subset shape already created for this DocTreeNode?
1116 AttributableShapeSharedPtr pSubset( maSubsetting.getSubsetShape( rTreeNode ) );
1118 // when true, this method has created a new subset
1119 // DrawShape
1120 bool bNewlyCreated( false );
1122 if( pSubset )
1124 o_rSubset = pSubset;
1126 // reusing existing subset
1128 else
1130 // not yet created, init entry
1131 o_rSubset.reset( new DrawShape( *this,
1132 rTreeNode,
1133 // TODO(Q3): That's a
1134 // hack. We assume
1135 // that start and end
1136 // index will always
1137 // be less than 65535
1138 mnPriority +
1139 rTreeNode.getStartIndex()/double(SAL_MAX_INT16) ));
1141 bNewlyCreated = true; // subset newly created
1144 // always register shape at DrawShapeSubsetting, to keep
1145 // refcount up-to-date
1146 maSubsetting.addSubsetShape( o_rSubset );
1148 // flush bounds cache
1149 maCurrentShapeUnitBounds.reset();
1151 return bNewlyCreated;
1154 bool DrawShape::revokeSubset( const AttributableShapeSharedPtr& rShape )
1156 // flush bounds cache
1157 maCurrentShapeUnitBounds.reset();
1159 // forward to delegate
1160 if( maSubsetting.revokeSubsetShape( rShape ) )
1162 // force redraw, our content has possibly changed (as
1163 // one of the subsets now display within our shape
1164 // again).
1165 mbForceUpdate = true;
1167 // #i47428# TEMP FIX: synchronize visibility of subset
1168 // with parent.
1170 // TODO(F3): Remove here, and implement
1171 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1172 // additional level of indirection: create a
1173 // persistent subset, containing all text/only the
1174 // background respectively. From _that_ object,
1175 // generate the temporary character subset shapes.
1176 const ShapeAttributeLayerSharedPtr& rAttrLayer(
1177 rShape->getTopmostAttributeLayer() );
1178 if( rAttrLayer &&
1179 rAttrLayer->isVisibilityValid() &&
1180 rAttrLayer->getVisibility() != isVisible() )
1182 const bool bVisibility( rAttrLayer->getVisibility() );
1184 // visibilities differ - adjust ours, then
1185 if( mpAttributeLayer )
1186 mpAttributeLayer->setVisibility( bVisibility );
1187 else
1188 mbIsVisible = bVisibility;
1191 // END TEMP FIX
1193 return true;
1196 return false;
1199 sal_Int32 DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1201 return maSubsetting.getNumberOfTreeNodes( eNodeType );
1204 DocTreeNode DrawShape::getTreeNode( sal_Int32 nNodeIndex,
1205 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1207 if ( hasHyperlinks())
1209 prepareHyperlinkIndices();
1212 return maSubsetting.getTreeNode( nNodeIndex, eNodeType );
1215 sal_Int32 DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode& rParentNode,
1216 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1218 return maSubsetting.getNumberOfSubsetTreeNodes( rParentNode, eNodeType );
1221 DocTreeNode DrawShape::getSubsetTreeNode( const DocTreeNode& rParentNode,
1222 sal_Int32 nNodeIndex,
1223 DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1225 return maSubsetting.getSubsetTreeNode( rParentNode, nNodeIndex, eNodeType );
1230 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */