diff options
Diffstat (limited to 'slideshow/source/engine/shapes/drawshape.cxx')
-rw-r--r-- | slideshow/source/engine/shapes/drawshape.cxx | 1478 |
1 files changed, 1478 insertions, 0 deletions
diff --git a/slideshow/source/engine/shapes/drawshape.cxx b/slideshow/source/engine/shapes/drawshape.cxx new file mode 100644 index 000000000000..dc8fe959a0e8 --- /dev/null +++ b/slideshow/source/engine/shapes/drawshape.cxx @@ -0,0 +1,1478 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_slideshow.hxx" + +// must be first +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <canvas/verbosetrace.hxx> + +#include <rtl/logfile.hxx> +#include <osl/diagnose.hxx> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <comphelper/anytostring.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/wrkwin.hxx> + +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/range/rangeexpander.hxx> + +#include <rtl/math.hxx> + +#include <com/sun/star/drawing/TextAnimationKind.hpp> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <tools/stream.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> + +#include <comphelper/scopeguard.hxx> +#include <canvas/canvastools.hxx> + +#include <cmath> // for trigonometry and fabs +#include <algorithm> +#include <functional> +#include <limits> + +#include "drawshapesubsetting.hxx" +#include "drawshape.hxx" +#include "eventqueue.hxx" +#include "wakeupevent.hxx" +#include "subsettableshapemanager.hxx" +#include "intrinsicanimationactivity.hxx" +#include "slideshowexceptions.hxx" +#include "tools.hxx" +#include "gdimtftools.hxx" +#include "drawinglayeranimation.hxx" + +#include <boost/bind.hpp> +#include <math.h> + +using namespace ::com::sun::star; + + +namespace slideshow +{ + namespace internal + { + //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100% + //metafiles are resolution dependent when bitmaps are contained with is the case for 3D scenes for example + //in addition a chart has resolution dependent content as it might skip points that are not visible for a given resolution (this is done for performance reasons) + bool local_getMetafileForChart( const uno::Reference< lang::XComponent >& xSource, + const uno::Reference< drawing::XDrawPage >& xContainingPage, + GDIMetaFile& rMtf ) + { + //get the chart model + uno::Reference< beans::XPropertySet > xPropSet( xSource, uno::UNO_QUERY ); + uno::Reference< frame::XModel > xChartModel; + getPropertyValue( xChartModel, xPropSet, OUSTR("Model")); + uno::Reference< lang::XMultiServiceFactory > xFact( xChartModel, uno::UNO_QUERY ); + OSL_ENSURE( xFact.is(), "Chart cannot be painted pretty!\n" ); + if(!xFact.is()) + return false; + + //get the chart view + uno::Reference< datatransfer::XTransferable > xChartViewTransferable( + xFact->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart2.ChartView" ) ) ), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xChartViewProp( xChartViewTransferable, uno::UNO_QUERY ); + OSL_ENSURE( xChartViewProp.is(), "Chart cannot be painted pretty!\n" ); + if( !xChartViewProp.is() ) + return false; + + //estimate zoom and resolution (this is only a workaround, correct would be to know and use the exact zoom and resoltion during slideshow display) + sal_Int32 nScaleXNumerator = 100;//zoom factor -> exact values are important for the quality of the created bitmap especially for 3D charts + sal_Int32 nScaleYNumerator = 100; + sal_Int32 nScaleXDenominator = 100; + sal_Int32 nScaleYDenominator = 100; + awt::Size aPixelPerChart( 1000, 1000 );//when data points happen to be on the same pixel as their predecessor no shape is created to safe performance + + Window* pActiveTopWindow( Application::GetActiveTopWindow() ); + WorkWindow* pWorkWindow( dynamic_cast<WorkWindow*>(pActiveTopWindow)); + if( pWorkWindow && pWorkWindow->IsPresentationMode() ) + { + Size aPixScreenSize( pActiveTopWindow->GetOutputSizePixel() ); + aPixelPerChart = awt::Size( aPixScreenSize.getWidth(), aPixScreenSize.getHeight() );//this is still to much (but costs only seldom performance), correct would be pixel per chart object + + uno::Reference< beans::XPropertySet > xPageProp( xContainingPage, uno::UNO_QUERY ); + sal_Int32 nLogicPageWidth=1; + sal_Int32 nLogicPageHeight=1; + if( getPropertyValue( nLogicPageWidth, xPageProp, OUSTR("Width")) && + getPropertyValue( nLogicPageHeight, xPageProp, OUSTR("Height")) ) + { + Size aLogicScreenSize( pActiveTopWindow->PixelToLogic( aPixScreenSize, MAP_100TH_MM ) ); + nScaleXNumerator = aLogicScreenSize.getWidth(); + nScaleYNumerator = aLogicScreenSize.getHeight(); + nScaleXDenominator = nLogicPageWidth; + nScaleYDenominator = nLogicPageHeight; + } + } + else + { + long nMaxPixWidth = 0; + long nMaxPixHeight = 0; + unsigned int nScreenCount( Application::GetScreenCount() ); + for( unsigned int nScreen=0; nScreen<nScreenCount; nScreen++ ) + { + Rectangle aCurScreenRect( Application::GetScreenPosSizePixel( nScreen ) ); + if( aCurScreenRect.GetWidth() > nMaxPixWidth ) + nMaxPixWidth = aCurScreenRect.GetWidth(); + if( aCurScreenRect.GetHeight() > nMaxPixHeight ) + nMaxPixHeight = aCurScreenRect.GetHeight(); + } + if(nMaxPixWidth>1 && nMaxPixHeight>1) + aPixelPerChart = awt::Size( nMaxPixWidth, nMaxPixHeight );//this is still to much (but costs only seldom performance), correct would be pixel per chart object + } + + try + { + uno::Sequence< beans::PropertyValue > aZoomFactors(4); + aZoomFactors[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleXNumerator") ); + aZoomFactors[0].Value = uno::makeAny( nScaleXNumerator ); + aZoomFactors[1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleXDenominator") ); + aZoomFactors[1].Value = uno::makeAny( nScaleXDenominator ); + aZoomFactors[2].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleYNumerator") ); + aZoomFactors[2].Value = uno::makeAny( nScaleYNumerator ); + aZoomFactors[3].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleYDenominator") ); + aZoomFactors[3].Value = uno::makeAny( nScaleYDenominator ); + + xChartViewProp->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ZoomFactors") ), uno::makeAny( aZoomFactors )); + xChartViewProp->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Resolution") ), uno::makeAny( aPixelPerChart )); + } + catch (uno::Exception &) + { + OSL_ENSURE( false, rtl::OUStringToOString( + comphelper::anyToString( + cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + + //get a metafile from the prepared chart view + datatransfer::DataFlavor aDataFlavor( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"") ), + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GDIMetaFile" ) ), + ::getCppuType( (const uno::Sequence< sal_Int8 >*) 0 ) ); + uno::Any aData( xChartViewTransferable->getTransferData( aDataFlavor ) ); + uno::Sequence< sal_Int8 > aSeq; + if( aData >>= aSeq ) + { + ::std::auto_ptr< SvMemoryStream > pSrcStm( new SvMemoryStream( (char*) aSeq.getConstArray(), aSeq.getLength(), STREAM_WRITE | STREAM_TRUNC ) ); + *(pSrcStm.get() ) >> rMtf; + return true; + } + return false; + } + + //same as getMetafile with an exception for charts + //for charts a metafile with a higher resolution is created, because charts have resolution dependent content + bool local_getMetaFile_WithSpecialChartHandling( const uno::Reference< lang::XComponent >& xSource, + const uno::Reference< drawing::XDrawPage >& xContainingPage, + GDIMetaFile& rMtf, + int mtfLoadFlags, + const uno::Reference< uno::XComponentContext >& rxContext ) + { + uno::Reference<beans::XPropertySet> xProp( xSource, uno::UNO_QUERY ); + rtl::OUString sCLSID; + getPropertyValue( sCLSID, xProp, OUSTR("CLSID")); + if( sCLSID.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("12DCAE26-281F-416F-a234-c3086127382e")) && local_getMetafileForChart( xSource, xContainingPage, rMtf ) ) + return true; + return getMetaFile( xSource, xContainingPage, rMtf, mtfLoadFlags, rxContext ); + } + + + ////////////////////////////////////////////////////////////////////// + // + // Private methods + // + ////////////////////////////////////////////////////////////////////// + + GDIMetaFileSharedPtr DrawShape::forceScrollTextMetaFile() + { + if ((mnCurrMtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != MTF_LOAD_SCROLL_TEXT_MTF) + { + // reload with added flags: + mpCurrMtf.reset( new GDIMetaFile ); + mnCurrMtfLoadFlags |= MTF_LOAD_SCROLL_TEXT_MTF; + local_getMetaFile_WithSpecialChartHandling( + uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY), + mxPage, *mpCurrMtf, mnCurrMtfLoadFlags, + mxComponentContext ); + + // TODO(F1): Currently, the scroll metafile will + // never contain any verbose text comments. Thus, + // can only display the full mtf content, no + // subsets. + maSubsetting.reset( mpCurrMtf ); + + // adapt maBounds. the requested scroll text metafile + // will typically have dimension different from the + // actual shape + ::basegfx::B2DRectangle aScrollRect, aPaintRect; + ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect, + aPaintRect, + mpCurrMtf ), + "DrawShape::forceScrollTextMetaFile(): Could " + "not extract scroll anim rectangles from mtf" ); + + // take the larger one of the two rectangles (that + // should be the bound rect of the retrieved + // metafile) + if( aScrollRect.isInside( aPaintRect ) ) + maBounds = aScrollRect; + else + maBounds = aPaintRect; + } + return mpCurrMtf; + } + + void DrawShape::updateStateIds() const + { + // Update the states, we've just redrawn or created a new + // attribute layer. + if( mpAttributeLayer ) + { + mnAttributeTransformationState = mpAttributeLayer->getTransformationState(); + mnAttributeClipState = mpAttributeLayer->getClipState(); + mnAttributeAlphaState = mpAttributeLayer->getAlphaState(); + mnAttributePositionState = mpAttributeLayer->getPositionState(); + mnAttributeContentState = mpAttributeLayer->getContentState(); + mnAttributeVisibilityState = mpAttributeLayer->getVisibilityState(); + } + } + + void DrawShape::ensureVerboseMtfComments() const + { + // TODO(F1): Text effects don't currently work for drawing + // layer animations. + + // only touch mpCurrMtf, if we're not a DrawingLayer + // animation. + if( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) == 0 && + maAnimationFrames.empty() ) + { + ENSURE_OR_THROW( !maSubsetting.hasSubsetShapes(), + "DrawShape::ensureVerboseMtfComments(): reloading the metafile " + "with active child subsets will wreak havoc on the view!" ); + ENSURE_OR_THROW( maSubsetting.getSubsetNode().isEmpty(), + "DrawShape::ensureVerboseMtfComments(): reloading the metafile " + "for an ALREADY SUBSETTED shape is not possible!" ); + + // re-fetch metafile with comments + // note that, in case of shapes without text, the new + // metafile might still not provide any useful + // subsetting information! + mpCurrMtf.reset( new GDIMetaFile ); + mnCurrMtfLoadFlags |= MTF_LOAD_VERBOSE_COMMENTS; + local_getMetaFile_WithSpecialChartHandling( + uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY), + mxPage, *mpCurrMtf, mnCurrMtfLoadFlags, + mxComponentContext ); + + maSubsetting.reset( maSubsetting.getSubsetNode(), + mpCurrMtf ); + } + } + + ViewShape::RenderArgs DrawShape::getViewRenderArgs() const + { + return ViewShape::RenderArgs( + maBounds, + getUpdateArea(), + getBounds(), + getActualUnitShapeBounds(), + mpAttributeLayer, + maSubsetting.getActiveSubsets(), + mnPriority); + } + + bool DrawShape::implRender( int nUpdateFlags ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShape::implRender()" ); + RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::presentation::internal::DrawShape: 0x%X", this ); + + // will perform the update now, clear update-enforcing + // flags + mbForceUpdate = false; + mbAttributeLayerRevoked = false; + + ENSURE_OR_RETURN_FALSE( !maViewShapes.empty(), + "DrawShape::implRender(): render called on DrawShape without views" ); + + if( maBounds.isEmpty() ) + { + // zero-sized shapes are effectively invisible, + // thus, we save us the rendering... + return true; + } + + // redraw all view shapes, by calling their update() method + if( ::std::count_if( maViewShapes.begin(), + maViewShapes.end(), + ::boost::bind<bool>( + ::boost::mem_fn( &ViewShape::update ), // though _theoretically_, + // bind should eat this even + // with _1 being a shared_ptr, + // it does _not_ for MSVC without + // the extra mem_fn. WTF. + _1, + ::boost::cref( mpCurrMtf ), + ::boost::cref( + getViewRenderArgs() ), + nUpdateFlags, + isVisible() ) ) + != static_cast<ViewShapeVector::difference_type>(maViewShapes.size()) ) + { + // at least one of the ViewShape::update() calls did return + // false - update failed on at least one ViewLayer + return false; + } + + // successfully redrawn - update state IDs to detect next changes + updateStateIds(); + + return true; + } + + int DrawShape::getUpdateFlags() const + { + // default: update nothing, unless ShapeAttributeStack + // tells us below, or if the attribute layer was revoked + int nUpdateFlags(ViewShape::NONE); + + // possibly the whole shape content changed + if( mbAttributeLayerRevoked ) + nUpdateFlags = ViewShape::CONTENT; + + + // determine what has to be updated + // -------------------------------- + + // do we have an attribute layer? + if( mpAttributeLayer ) + { + // Prevent nUpdateFlags to be modified when the shape is not + // visible, except when it just was hidden. + if (mpAttributeLayer->getVisibility() + || mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState ) + { + if (mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState ) + { + // Change of the visibility state is mapped to + // content change because when the visibility + // changes then usually a sprite is shown or hidden + // and the background under has to be painted once. + nUpdateFlags |= ViewShape::CONTENT; + } + + // TODO(P1): This can be done without conditional branching. + // See HAKMEM. + if( mpAttributeLayer->getPositionState() != mnAttributePositionState ) + { + nUpdateFlags |= ViewShape::POSITION; + } + if( mpAttributeLayer->getAlphaState() != mnAttributeAlphaState ) + { + nUpdateFlags |= ViewShape::ALPHA; + } + if( mpAttributeLayer->getClipState() != mnAttributeClipState ) + { + nUpdateFlags |= ViewShape::CLIP; + } + if( mpAttributeLayer->getTransformationState() != mnAttributeTransformationState ) + { + nUpdateFlags |= ViewShape::TRANSFORMATION; + } + if( mpAttributeLayer->getContentState() != mnAttributeContentState ) + { + nUpdateFlags |= ViewShape::CONTENT; + } + } + } + + return nUpdateFlags; + } + + ::basegfx::B2DRectangle DrawShape::getActualUnitShapeBounds() const + { + ENSURE_OR_THROW( !maViewShapes.empty(), + "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" ); + + const VectorOfDocTreeNodes& rSubsets( + maSubsetting.getActiveSubsets() ); + + const ::basegfx::B2DRectangle aDefaultBounds( 0.0,0.0,1.0,1.0 ); + + // perform the cheapest check first + if( rSubsets.empty() ) + { + // if subset contains the whole shape, no need to call + // the somewhat expensive bound calculation, since as + // long as the subset is empty, this branch will be + // taken. + return aDefaultBounds; + } + else + { + OSL_ENSURE( rSubsets.size() != 1 || + !rSubsets.front().isEmpty(), + "DrawShape::getActualUnitShapeBounds() expects a " + "_non-empty_ subset vector for a subsetted shape!" ); + + // are the cached bounds still valid? + if( !maCurrentShapeUnitBounds ) + { + // no, (re)generate them + // ===================== + + // setup cached values to defaults (might fail to + // retrieve true bounds below) + maCurrentShapeUnitBounds.reset( aDefaultBounds ); + + // TODO(P2): the subset of the master shape (that from + // which the subsets are subtracted) changes + // relatively often (every time a subset shape is + // added or removed). Maybe we should exclude it here, + // always assuming full bounds? + + ::cppcanvas::CanvasSharedPtr pDestinationCanvas( + maViewShapes.front()->getViewLayer()->getCanvas() ); + + // TODO(Q2): Although this _is_ currently + // view-agnostic, it might not stay like + // that. Maybe this method should again be moved + // to the ViewShape + ::cppcanvas::RendererSharedPtr pRenderer( + maViewShapes.front()->getRenderer( + pDestinationCanvas, mpCurrMtf, mpAttributeLayer ) ); + + // If we cannot not prefetch, be defensive and assume + // full shape size + if( pRenderer ) + { + // temporarily, switch total transformation to identity + // (need the bounds in the [0,1]x[0,1] unit coordinate + // system. + ::basegfx::B2DHomMatrix aEmptyTransformation; + + ::basegfx::B2DHomMatrix aOldTransform( pDestinationCanvas->getTransformation() ); + pDestinationCanvas->setTransformation( aEmptyTransformation ); + pRenderer->setTransformation( aEmptyTransformation ); + + // restore old transformation when leaving the scope + const ::comphelper::ScopeGuard aGuard( + boost::bind( &::cppcanvas::Canvas::setTransformation, + pDestinationCanvas, aOldTransform ) ); + + + // retrieve bounds for subset of whole metafile + // -------------------------------------------- + + ::basegfx::B2DRange aTotalBounds; + + // cannot use ::boost::bind, ::basegfx::B2DRange::expand() + // is overloaded. + VectorOfDocTreeNodes::const_iterator aCurr( rSubsets.begin() ); + const VectorOfDocTreeNodes::const_iterator aEnd( rSubsets.end() ); + while( aCurr != aEnd ) + { + aTotalBounds.expand( pRenderer->getSubsetArea( + aCurr->getStartIndex(), + aCurr->getEndIndex() ) ); + ++aCurr; + } + + OSL_ENSURE( aTotalBounds.getMinX() >= -0.1 && + aTotalBounds.getMinY() >= -0.1 && + aTotalBounds.getMaxX() <= 1.1 && + aTotalBounds.getMaxY() <= 1.1, + "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" ); + + // really make sure no shape appears larger than its + // original bounds (there _are_ some pathologic cases, + // especially when imported from PPT, that have + // e.g. obscenely large polygon bounds) + aTotalBounds.intersect( + ::basegfx::B2DRange( 0.0, 0.0, + 1.0, 1.0 )); + + maCurrentShapeUnitBounds.reset( aTotalBounds ); + } + } + + return *maCurrentShapeUnitBounds; + } + } + + DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape, + const uno::Reference< drawing::XDrawPage >& xContainingPage, + double nPrio, + bool bForeignSource, + const SlideShowContext& rContext ) : + mxShape( xShape ), + mxPage( xContainingPage ), + maAnimationFrames(), // empty, we don't have no intrinsic animation + mnCurrFrame(0), + mpCurrMtf(), + mnCurrMtfLoadFlags( bForeignSource + ? MTF_LOAD_FOREIGN_SOURCE : MTF_LOAD_NONE ), + maCurrentShapeUnitBounds(), + mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ), + maBounds( getAPIShapeBounds( xShape ) ), + mpAttributeLayer(), + mpIntrinsicAnimationActivity(), + mnAttributeTransformationState(0), + mnAttributeClipState(0), + mnAttributeAlphaState(0), + mnAttributePositionState(0), + mnAttributeContentState(0), + mnAttributeVisibilityState(0), + maViewShapes(), + mxComponentContext( rContext.mxComponentContext ), + maHyperlinkIndices(), + maHyperlinkRegions(), + maSubsetting(), + mnIsAnimatedCount(0), + mnAnimationLoopCount(0), + meCycleMode(CYCLE_LOOP), + mbIsVisible( true ), + mbForceUpdate( false ), + mbAttributeLayerRevoked( false ), + mbDrawingLayerAnim( false ) + { + ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" ); + ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" ); + + // check for drawing layer animations: + drawing::TextAnimationKind eKind = drawing::TextAnimationKind_NONE; + uno::Reference<beans::XPropertySet> xPropSet( mxShape, + uno::UNO_QUERY ); + if( xPropSet.is() ) + getPropertyValue( eKind, xPropSet, + OUSTR("TextAnimationKind") ); + mbDrawingLayerAnim = (eKind != drawing::TextAnimationKind_NONE); + + // must NOT be called from within initializer list, uses + // state from mnCurrMtfLoadFlags! + mpCurrMtf.reset( new GDIMetaFile ); + local_getMetaFile_WithSpecialChartHandling( + uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY), + xContainingPage, *mpCurrMtf, mnCurrMtfLoadFlags, + mxComponentContext ); + ENSURE_OR_THROW( mpCurrMtf, + "DrawShape::DrawShape(): Invalid metafile" ); + maSubsetting.reset( mpCurrMtf ); + + prepareHyperlinkIndices(); + } + + DrawShape::DrawShape( const uno::Reference< drawing::XShape >& xShape, + const uno::Reference< drawing::XDrawPage >& xContainingPage, + double nPrio, + const Graphic& rGraphic, + const SlideShowContext& rContext ) : + mxShape( xShape ), + mxPage( xContainingPage ), + maAnimationFrames(), + mnCurrFrame(0), + mpCurrMtf(), + mnCurrMtfLoadFlags( MTF_LOAD_NONE ), + maCurrentShapeUnitBounds(), + mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ), + maBounds( getAPIShapeBounds( xShape ) ), + mpAttributeLayer(), + mpIntrinsicAnimationActivity(), + mnAttributeTransformationState(0), + mnAttributeClipState(0), + mnAttributeAlphaState(0), + mnAttributePositionState(0), + mnAttributeContentState(0), + mnAttributeVisibilityState(0), + maViewShapes(), + mxComponentContext( rContext.mxComponentContext ), + maHyperlinkIndices(), + maHyperlinkRegions(), + maSubsetting(), + mnIsAnimatedCount(0), + mnAnimationLoopCount(0), + meCycleMode(CYCLE_LOOP), + mbIsVisible( true ), + mbForceUpdate( false ), + mbAttributeLayerRevoked( false ), + mbDrawingLayerAnim( false ) + { + ENSURE_OR_THROW( rGraphic.IsAnimated(), + "DrawShape::DrawShape(): Graphic is no animation" ); + + getAnimationFromGraphic( maAnimationFrames, + mnAnimationLoopCount, + meCycleMode, + rGraphic ); + + ENSURE_OR_THROW( !maAnimationFrames.empty() && + maAnimationFrames.front().mpMtf, + "DrawShape::DrawShape(): " ); + mpCurrMtf = maAnimationFrames.front().mpMtf; + + ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" ); + ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" ); + ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" ); + } + + DrawShape::DrawShape( const DrawShape& rSrc, + const DocTreeNode& rTreeNode, + double nPrio ) : + mxShape( rSrc.mxShape ), + mxPage( rSrc.mxPage ), + maAnimationFrames(), // don't copy animations for subsets, + // only the current frame! + mnCurrFrame(0), + mpCurrMtf( rSrc.mpCurrMtf ), + mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ), + maCurrentShapeUnitBounds(), + mnPriority( nPrio ), + maBounds( rSrc.maBounds ), + mpAttributeLayer(), + mpIntrinsicAnimationActivity(), + mnAttributeTransformationState(0), + mnAttributeClipState(0), + mnAttributeAlphaState(0), + mnAttributePositionState(0), + mnAttributeContentState(0), + mnAttributeVisibilityState(0), + maViewShapes(), + mxComponentContext( rSrc.mxComponentContext ), + maHyperlinkIndices(), + maHyperlinkRegions(), + maSubsetting( rTreeNode, mpCurrMtf ), + mnIsAnimatedCount(0), + mnAnimationLoopCount(0), + meCycleMode(CYCLE_LOOP), + mbIsVisible( rSrc.mbIsVisible ), + mbForceUpdate( false ), + mbAttributeLayerRevoked( false ), + mbDrawingLayerAnim( false ) + { + ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" ); + ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" ); + + // xxx todo: currently not implemented for subsetted shapes; + // would mean modifying set of hyperlink regions when + // subsetting text portions. N.B.: there's already an + // issue for this #i72828# + } + + ////////////////////////////////////////////////////////////////////// + // + // Public methods + // + ////////////////////////////////////////////////////////////////////// + + DrawShapeSharedPtr DrawShape::create( + const uno::Reference< drawing::XShape >& xShape, + const uno::Reference< drawing::XDrawPage >& xContainingPage, + double nPrio, + bool bForeignSource, + const SlideShowContext& rContext ) + { + DrawShapeSharedPtr pShape( new DrawShape(xShape, + xContainingPage, + nPrio, + bForeignSource, + rContext) ); + + if( pShape->hasIntrinsicAnimation() ) + { + OSL_ASSERT( pShape->maAnimationFrames.empty() ); + if( pShape->getNumberOfTreeNodes( + DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH) > 0 ) + { + pShape->mpIntrinsicAnimationActivity = + createDrawingLayerAnimActivity( + rContext, + pShape); + } + } + + if( pShape->hasHyperlinks() ) + rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape ); + + return pShape; + } + + DrawShapeSharedPtr DrawShape::create( + const uno::Reference< drawing::XShape >& xShape, + const uno::Reference< drawing::XDrawPage >& xContainingPage, + double nPrio, + const Graphic& rGraphic, + const SlideShowContext& rContext ) + { + DrawShapeSharedPtr pShape( new DrawShape(xShape, + xContainingPage, + nPrio, + rGraphic, + rContext) ); + + if( pShape->hasIntrinsicAnimation() ) + { + OSL_ASSERT( !pShape->maAnimationFrames.empty() ); + + std::vector<double> aTimeout; + std::transform( + pShape->maAnimationFrames.begin(), + pShape->maAnimationFrames.end(), + std::back_insert_iterator< std::vector<double> >( aTimeout ), + boost::mem_fn(&MtfAnimationFrame::getDuration) ); + + WakeupEventSharedPtr pWakeupEvent( + new WakeupEvent( rContext.mrEventQueue.getTimer(), + rContext.mrActivitiesQueue ) ); + + ActivitySharedPtr pActivity = + createIntrinsicAnimationActivity( + rContext, + pShape, + pWakeupEvent, + aTimeout, + pShape->mnAnimationLoopCount, + pShape->meCycleMode); + + pWakeupEvent->setActivity( pActivity ); + pShape->mpIntrinsicAnimationActivity = pActivity; + } + + OSL_ENSURE( !pShape->hasHyperlinks(), + "DrawShape::create(): graphic-only shapes must not have hyperlinks!" ); + + return pShape; + } + + DrawShape::~DrawShape() + { + try + { + // dispose intrinsic animation activity, else, it will + // linger forever + ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() ); + if( pActivity ) + pActivity->dispose(); + } + catch (uno::Exception &) + { + OSL_ENSURE( false, rtl::OUStringToOString( + comphelper::anyToString( + cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + + uno::Reference< drawing::XShape > DrawShape::getXShape() const + { + return mxShape; + } + + void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ) + { + ViewShapeVector::iterator aEnd( maViewShapes.end() ); + + // already added? + if( ::std::find_if( maViewShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewShape::getViewLayer, + _1 ), + ::boost::cref( rNewLayer ) ) ) != aEnd ) + { + // yes, nothing to do + return; + } + + ViewShapeSharedPtr pNewShape( new ViewShape( rNewLayer ) ); + + maViewShapes.push_back( pNewShape ); + + // pass on animation state + if( mnIsAnimatedCount ) + { + for( int i=0; i<mnIsAnimatedCount; ++i ) + pNewShape->enterAnimationMode(); + } + + // render the Shape on the newly added ViewLayer + if( bRedrawLayer ) + { + pNewShape->update( mpCurrMtf, + getViewRenderArgs(), + ViewShape::FORCE, + isVisible() ); + } + } + + bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer ) + { + const ViewShapeVector::iterator aEnd( maViewShapes.end() ); + + OSL_ENSURE( ::std::count_if(maViewShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewShape::getViewLayer, + _1 ), + ::boost::cref( rLayer ) ) ) < 2, + "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" ); + + ViewShapeVector::iterator aIter; + + if( (aIter=::std::remove_if( maViewShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewShape::getViewLayer, + _1 ), + ::boost::cref( rLayer ) ) )) == aEnd ) + { + // view layer seemingly was not added, failed + return false; + } + + // actually erase from container + maViewShapes.erase( aIter, aEnd ); + + return true; + } + + bool DrawShape::clearAllViewLayers() + { + maViewShapes.clear(); + return true; + } + + bool DrawShape::update() const + { + if( mbForceUpdate ) + { + return render(); + } + else + { + return implRender( getUpdateFlags() ); + } + } + + bool DrawShape::render() const + { + // force redraw. Have to also pass on the update flags, + // because e.g. content update (regeneration of the + // metafile renderer) is normally not performed. A simple + // ViewShape::FORCE would only paint the metafile in its + // old state. + return implRender( ViewShape::FORCE | getUpdateFlags() ); + } + + bool DrawShape::isContentChanged() const + { + return mbForceUpdate ? + true : + getUpdateFlags() != ViewShape::NONE; + } + + + ::basegfx::B2DRectangle DrawShape::getBounds() const + { + // little optimization: for non-modified shapes, we don't + // create an ShapeAttributeStack, and therefore also don't + // have to check it. + return getShapePosSize( maBounds, + mpAttributeLayer ); + } + + ::basegfx::B2DRectangle DrawShape::getDomBounds() const + { + return maBounds; + } + + namespace + { + /** Functor expanding AA border for each passed ViewShape + + Could not use ::boost::bind here, since + B2DRange::expand is overloaded (which yields one or + the other template type deduction ambiguous) + */ + class Expander + { + public: + Expander( ::basegfx::B2DSize& rBounds ) : + mrBounds( rBounds ) + { + } + + void operator()( const ViewShapeSharedPtr& rShape ) const + { + const ::basegfx::B2DSize& rShapeBorder( rShape->getAntialiasingBorder() ); + + mrBounds.setX( + ::std::max( + rShapeBorder.getX(), + mrBounds.getX() ) ); + mrBounds.setY( + ::std::max( + rShapeBorder.getY(), + mrBounds.getY() ) ); + } + + private: + ::basegfx::B2DSize& mrBounds; + }; + } + + ::basegfx::B2DRectangle DrawShape::getUpdateArea() const + { + ::basegfx::B2DRectangle aBounds; + + // an already empty shape bound need no further + // treatment. In fact, any changes applied below would + // actually remove the special empty state, thus, don't + // change! + if( !maBounds.isEmpty() ) + { + basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0); + + if( !maViewShapes.empty() ) + aUnitBounds = getActualUnitShapeBounds(); + + if( !aUnitBounds.isEmpty() ) + { + if( mpAttributeLayer ) + { + // calc actual shape area (in user coordinate + // space) from the transformation as given by the + // shape attribute layer + aBounds = getShapeUpdateArea( aUnitBounds, + getShapeTransformation( getBounds(), + mpAttributeLayer ), + mpAttributeLayer ); + } + else + { + // no attribute layer, thus, the true shape bounds + // can be directly derived from the XShape bound + // attribute + aBounds = getShapeUpdateArea( aUnitBounds, + maBounds ); + } + + if( !maViewShapes.empty() ) + { + // determine border needed for antialiasing the shape + ::basegfx::B2DSize aAABorder(0.0,0.0); + + // for every view, get AA border and 'expand' aAABorder + // appropriately. + ::std::for_each( maViewShapes.begin(), + maViewShapes.end(), + Expander( aAABorder ) ); + + // add calculated AA border to aBounds + aBounds = ::basegfx::B2DRectangle( aBounds.getMinX() - aAABorder.getX(), + aBounds.getMinY() - aAABorder.getY(), + aBounds.getMaxX() + aAABorder.getX(), + aBounds.getMaxY() + aAABorder.getY() ); + } + } + } + + return aBounds; + } + + bool DrawShape::isVisible() const + { + bool bIsVisible( mbIsVisible ); + + if( mpAttributeLayer ) + { + // check whether visibility and alpha are not default + // (mpAttributeLayer->isVisibilityValid() returns true + // then): bVisible becomes true, if shape visibility + // is on and alpha is not 0.0 (fully transparent) + if( mpAttributeLayer->isVisibilityValid() ) + bIsVisible = mpAttributeLayer->getVisibility(); + + // only touch bIsVisible, if the shape is still + // visible - if getVisibility already made us + // invisible, no alpha value will make us appear + // again. + if( bIsVisible && mpAttributeLayer->isAlphaValid() ) + bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() ); + } + + return bIsVisible; + } + + double DrawShape::getPriority() const + { + return mnPriority; + } + + bool DrawShape::isBackgroundDetached() const + { + return mnIsAnimatedCount > 0; + } + + bool DrawShape::hasIntrinsicAnimation() const + { + return (!maAnimationFrames.empty() || mbDrawingLayerAnim); + } + + bool DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame ) + { + ENSURE_OR_RETURN_FALSE( nCurrFrame < maAnimationFrames.size(), + "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" ); + + if( mnCurrFrame != nCurrFrame ) + { + mnCurrFrame = nCurrFrame; + mpCurrMtf = maAnimationFrames[ mnCurrFrame ].mpMtf; + mbForceUpdate = true; + } + + return true; + } + + // hyperlink support + void DrawShape::prepareHyperlinkIndices() const + { + if ( !maHyperlinkIndices.empty()) + { + maHyperlinkIndices.clear(); + maHyperlinkRegions.clear(); + } + + sal_Int32 nIndex = 0; + for ( MetaAction * pCurrAct = mpCurrMtf->FirstAction(); + pCurrAct != 0; pCurrAct = mpCurrMtf->NextAction() ) + { + if (pCurrAct->GetType() == META_COMMENT_ACTION) { + MetaCommentAction * pAct = + static_cast<MetaCommentAction *>(pCurrAct); + // skip comment if not a special XTEXT comment + if (pAct->GetComment().CompareIgnoreCaseToAscii( + RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_BEGIN") ) == + COMPARE_EQUAL && + // e.g. date field doesn't have data! + // currently assuming that only url field, this is + // somehow fragile! xxx todo if possible + pAct->GetData() != 0 && + pAct->GetDataSize() > 0) + { + if (!maHyperlinkIndices.empty() && + maHyperlinkIndices.back().second == -1) { + OSL_ENSURE( false, "### pending FIELD_SEQ_END!" ); + maHyperlinkIndices.pop_back(); + maHyperlinkRegions.pop_back(); + } + maHyperlinkIndices.push_back( + HyperlinkIndexPair( nIndex + 1, + -1 /* to be filled below */ ) ); + maHyperlinkRegions.push_back( + HyperlinkRegion( + basegfx::B2DRectangle(), + rtl::OUString( + reinterpret_cast<sal_Unicode const*>( + pAct->GetData()), + pAct->GetDataSize() / sizeof(sal_Unicode) ) + ) ); + } + else if (pAct->GetComment().CompareIgnoreCaseToAscii( + RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_END")) == + COMPARE_EQUAL && + // pending end is expected: + !maHyperlinkIndices.empty() && + maHyperlinkIndices.back().second == -1) + { + maHyperlinkIndices.back().second = nIndex; + } + ++nIndex; + } + else + nIndex += getNextActionOffset(pCurrAct); + } + if (!maHyperlinkIndices.empty() && + maHyperlinkIndices.back().second == -1) { + OSL_ENSURE( false, "### pending FIELD_SEQ_END!" ); + maHyperlinkIndices.pop_back(); + maHyperlinkRegions.pop_back(); + } + OSL_ASSERT( maHyperlinkIndices.size() == maHyperlinkRegions.size()); + } + + bool DrawShape::hasHyperlinks() const + { + return ! maHyperlinkRegions.empty(); + } + + HyperlinkArea::HyperlinkRegions DrawShape::getHyperlinkRegions() const + { + OSL_ASSERT( !maViewShapes.empty() ); + + if( !isVisible() ) + return HyperlinkArea::HyperlinkRegions(); + + // late init, determine regions: + if( !maHyperlinkRegions.empty() && + !maViewShapes.empty() && + // region already inited? + maHyperlinkRegions.front().first.getWidth() == 0 && + maHyperlinkRegions.front().first.getHeight() == 0 && + maHyperlinkRegions.size() == maHyperlinkIndices.size() ) + { + // TODO(Q2): Although this _is_ currently + // view-agnostic, it might not stay like that. + ViewShapeSharedPtr const& pViewShape = maViewShapes.front(); + cppcanvas::CanvasSharedPtr const pCanvas( + pViewShape->getViewLayer()->getCanvas() ); + + // reuse Renderer of first view shape: + cppcanvas::RendererSharedPtr const pRenderer( + pViewShape->getRenderer( + pCanvas, mpCurrMtf, mpAttributeLayer ) ); + + OSL_ASSERT( pRenderer ); + + if (pRenderer) + { + basegfx::B2DHomMatrix const aOldTransform( + pCanvas->getTransformation() ); + basegfx::B2DHomMatrix aTransform; + pCanvas->setTransformation( aTransform /* empty */ ); + + comphelper::ScopeGuard const resetOldTransformation( + boost::bind( &cppcanvas::Canvas::setTransformation, + pCanvas.get(), + boost::cref(aOldTransform) )); + + aTransform.scale( maBounds.getWidth(), + maBounds.getHeight() ); + pRenderer->setTransformation( aTransform ); + pRenderer->setClip(); + + for( std::size_t pos = maHyperlinkRegions.size(); pos--; ) + { + // get region: + HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos]; + basegfx::B2DRectangle const region( + pRenderer->getSubsetArea( rIndices.first, + rIndices.second )); + maHyperlinkRegions[pos].first = region; + } + } + } + + // shift shape-relative hyperlink regions to + // slide-absolute position + + HyperlinkRegions aTranslatedRegions; + const basegfx::B2DPoint& rOffset(getBounds().getMinimum()); + HyperlinkRegions::const_iterator aIter( maHyperlinkRegions.begin() ); + HyperlinkRegions::const_iterator const aEnd ( maHyperlinkRegions.end() ); + while( aIter != aEnd ) + { + basegfx::B2DRange const& relRegion( aIter->first ); + aTranslatedRegions.push_back( + std::make_pair( + basegfx::B2DRange( + relRegion.getMinimum() + rOffset, + relRegion.getMaximum() + rOffset), + aIter->second) ); + ++aIter; + } + + return aTranslatedRegions; + } + + double DrawShape::getHyperlinkPriority() const + { + return getPriority(); + } + + + // AnimatableShape methods + // ====================================================== + + void DrawShape::enterAnimationMode() + { + OSL_ENSURE( !maViewShapes.empty(), + "DrawShape::enterAnimationMode(): called on DrawShape without views" ); + + if( mnIsAnimatedCount == 0 ) + { + // notify all ViewShapes, by calling their enterAnimationMode method. + // We're now entering animation mode + ::std::for_each( maViewShapes.begin(), + maViewShapes.end(), + ::boost::mem_fn( &ViewShape::enterAnimationMode ) ); + } + + ++mnIsAnimatedCount; + } + + void DrawShape::leaveAnimationMode() + { + OSL_ENSURE( !maViewShapes.empty(), + "DrawShape::leaveAnimationMode(): called on DrawShape without views" ); + + --mnIsAnimatedCount; + + if( mnIsAnimatedCount == 0 ) + { + // notify all ViewShapes, by calling their leaveAnimationMode method. + // we're now leaving animation mode + ::std::for_each( maViewShapes.begin(), + maViewShapes.end(), + ::boost::mem_fn( &ViewShape::leaveAnimationMode ) ); + } + } + + + // AttributableShape methods + // ====================================================== + + ShapeAttributeLayerSharedPtr DrawShape::createAttributeLayer() + { + // create new layer, with last as its new child + mpAttributeLayer.reset( new ShapeAttributeLayer( mpAttributeLayer ) ); + + // Update the local state ids to reflect those of the new layer. + updateStateIds(); + + return mpAttributeLayer; + } + + bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr& rLayer ) + { + if( !mpAttributeLayer ) + return false; // no layers + + if( mpAttributeLayer == rLayer ) + { + // it's the toplevel layer + mpAttributeLayer = mpAttributeLayer->getChildLayer(); + + // force content redraw, all state variables have + // possibly changed + mbAttributeLayerRevoked = true; + + return true; + } + else + { + // pass on to the layer, to try its children + return mpAttributeLayer->revokeChildLayer( rLayer ); + } + } + + ShapeAttributeLayerSharedPtr DrawShape::getTopmostAttributeLayer() const + { + return mpAttributeLayer; + } + + void DrawShape::setVisibility( bool bVisible ) + { + if( mbIsVisible != bVisible ) + { + mbIsVisible = bVisible; + mbForceUpdate = true; + } + } + + const DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier() const + { + return *this; + } + + DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier() + { + return *this; + } + + DocTreeNode DrawShape::getSubsetNode() const + { + ensureVerboseMtfComments(); + + // forward to delegate + return maSubsetting.getSubsetNode(); + } + + AttributableShapeSharedPtr DrawShape::getSubset( const DocTreeNode& rTreeNode ) const + { + ENSURE_OR_THROW( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) != 0, + "DrawShape::getSubset(): subset query on shape with apparently no subsets" ); + + // forward to delegate + return maSubsetting.getSubsetShape( rTreeNode ); + } + + bool DrawShape::createSubset( AttributableShapeSharedPtr& o_rSubset, + const DocTreeNode& rTreeNode ) + { + ENSURE_OR_THROW( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) != 0, + "DrawShape::createSubset(): subset query on shape with apparently no subsets" ); + + // subset shape already created for this DocTreeNode? + AttributableShapeSharedPtr pSubset( maSubsetting.getSubsetShape( rTreeNode ) ); + + // when true, this method has created a new subset + // DrawShape + bool bNewlyCreated( false ); + + if( pSubset ) + { + o_rSubset = pSubset; + + // reusing existing subset + } + else + { + // not yet created, init entry + o_rSubset.reset( new DrawShape( *this, + rTreeNode, + // TODO(Q3): That's a + // hack. We assume + // that start and end + // index will always + // be less than 65535 + mnPriority + + rTreeNode.getStartIndex()/double(SAL_MAX_INT16) )); + + bNewlyCreated = true; // subset newly created + } + + // always register shape at DrawShapeSubsetting, to keep + // refcount up-to-date + maSubsetting.addSubsetShape( o_rSubset ); + + // flush bounds cache + maCurrentShapeUnitBounds.reset(); + + return bNewlyCreated; + } + + bool DrawShape::revokeSubset( const AttributableShapeSharedPtr& rShape ) + { + ENSURE_OR_THROW( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) != 0, + "DrawShape::createSubset(): subset query on shape with apparently no subsets" ); + + // flush bounds cache + maCurrentShapeUnitBounds.reset(); + + // forward to delegate + if( maSubsetting.revokeSubsetShape( rShape ) ) + { + // force redraw, our content has possibly changed (as + // one of the subsets now display within our shape + // again). + mbForceUpdate = true; + + // #i47428# TEMP FIX: synchronize visibility of subset + // with parent. + + // TODO(F3): Remove here, and implement + // TEXT_ONLY/BACKGROUND_ONLY with the proverbial + // additional level of indirection: create a + // persistent subset, containing all text/only the + // background respectively. From _that_ object, + // generate the temporary character subset shapes. + const ShapeAttributeLayerSharedPtr& rAttrLayer( + rShape->getTopmostAttributeLayer() ); + if( rAttrLayer && + rAttrLayer->isVisibilityValid() && + rAttrLayer->getVisibility() != isVisible() ) + { + const bool bVisibility( rAttrLayer->getVisibility() ); + + // visibilities differ - adjust ours, then + if( mpAttributeLayer ) + mpAttributeLayer->setVisibility( bVisibility ); + else + mbIsVisible = bVisibility; + } + + // END TEMP FIX + + return true; + } + + return false; + } + + sal_Int32 DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException + { + ensureVerboseMtfComments(); + + return maSubsetting.getNumberOfTreeNodes( eNodeType ); + } + + DocTreeNode DrawShape::getTreeNode( sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException + { + ensureVerboseMtfComments(); + + if ( hasHyperlinks()) + { + prepareHyperlinkIndices(); + } + + return maSubsetting.getTreeNode( nNodeIndex, eNodeType ); + } + + sal_Int32 DrawShape::getNumberOfSubsetTreeNodes ( const DocTreeNode& rParentNode, + DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException + { + ensureVerboseMtfComments(); + + return maSubsetting.getNumberOfSubsetTreeNodes( rParentNode, eNodeType ); + } + + DocTreeNode DrawShape::getSubsetTreeNode( const DocTreeNode& rParentNode, + sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException + { + ensureVerboseMtfComments(); + + return maSubsetting.getSubsetTreeNode( rParentNode, nNodeIndex, eNodeType ); + } + } +} |