| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 |
| { |
| ////////////////////////////////////////////////////////////////////// |
| // |
| // 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; |
| getMetaFile( |
| 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; |
| getMetaFile( |
| 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 ); |
| getMetaFile( |
| 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 ); |
| } |
| } |
| } |