diff options
Diffstat (limited to 'slideshow/source/engine/shapes')
28 files changed, 10225 insertions, 0 deletions
diff --git a/slideshow/source/engine/shapes/appletshape.cxx b/slideshow/source/engine/shapes/appletshape.cxx new file mode 100644 index 000000000000..4b1cb5398fee --- /dev/null +++ b/slideshow/source/engine/shapes/appletshape.cxx @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <canvas/verbosetrace.hxx> +#include <canvas/canvastools.hxx> + +#include <boost/shared_ptr.hpp> + +#include "appletshape.hxx" +#include "externalshapebase.hxx" +#include "vieweventhandler.hxx" +#include "viewappletshape.hxx" +#include "tools.hxx" + +#include <boost/bind.hpp> +#include <algorithm> + + +using namespace ::com::sun::star; + + +namespace slideshow +{ + namespace internal + { + /** Represents an applet shape. + + This implementation offers support for applet shapes (both + Java applets, and Netscape plugins). Such shapes need + special treatment. + */ + class AppletShape : public ExternalShapeBase + { + public: + /** Create a shape for the given XShape for a applet object + + @param xShape + The XShape to represent. + + @param nPrio + Externally-determined shape priority (used e.g. for + paint ordering). This number _must be_ unique! + + @param rServiceName + Service name to use, when creating the actual viewer + component + + @param pPropCopyTable + Table of plain ASCII property names, to copy from + xShape to applet. + + @param nNumPropEntries + Number of property table entries (in pPropCopyTable) + */ + AppletShape( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + double nPrio, + const ::rtl::OUString& rServiceName, + const char** pPropCopyTable, + sal_Size nNumPropEntries, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + + private: + + // View layer methods + //------------------------------------------------------------------ + + virtual void addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ); + virtual bool removeViewLayer( const ViewLayerSharedPtr& rNewLayer ); + virtual bool clearAllViewLayers(); + + + // ExternalShapeBase methods + //------------------------------------------------------------------ + + virtual bool implRender( const ::basegfx::B2DRange& rCurrBounds ) const; + virtual void implViewChanged( const UnoViewSharedPtr& rView ); + virtual void implViewsChanged(); + virtual bool implStartIntrinsicAnimation(); + virtual bool implEndIntrinsicAnimation(); + virtual bool implPauseIntrinsicAnimation(); + virtual bool implIsIntrinsicAnimationPlaying() const; + virtual void implSetIntrinsicAnimationTime(double); + + const ::rtl::OUString maServiceName; + const char** mpPropCopyTable; + const sal_Size mnNumPropEntries; + + /// the list of active view shapes (one for each registered view layer) + typedef ::std::vector< ViewAppletShapeSharedPtr > ViewAppletShapeVector; + ViewAppletShapeVector maViewAppletShapes; + bool mbIsPlaying; + }; + + AppletShape::AppletShape( const uno::Reference< drawing::XShape >& xShape, + double nPrio, + const ::rtl::OUString& rServiceName, + const char** pPropCopyTable, + sal_Size nNumPropEntries, + const SlideShowContext& rContext ) : + ExternalShapeBase( xShape, nPrio, rContext ), + maServiceName( rServiceName ), + mpPropCopyTable( pPropCopyTable ), + mnNumPropEntries( nNumPropEntries ), + maViewAppletShapes(), + mbIsPlaying(false) + { + } + + // --------------------------------------------------------------------- + + void AppletShape::implViewChanged( const UnoViewSharedPtr& rView ) + { + // determine ViewAppletShape that needs update + ViewAppletShapeVector::const_iterator aIter(maViewAppletShapes.begin()); + ViewAppletShapeVector::const_iterator const aEnd (maViewAppletShapes.end()); + while( aIter != aEnd ) + { + if( (*aIter)->getViewLayer()->isOnView(rView) ) + (*aIter)->resize(getBounds()); + + ++aIter; + } + } + + // --------------------------------------------------------------------- + + void AppletShape::implViewsChanged() + { + // resize all ViewShapes + ::std::for_each( maViewAppletShapes.begin(), + maViewAppletShapes.end(), + ::boost::bind( + &ViewAppletShape::resize, + _1, + ::boost::cref( AppletShape::getBounds())) ); + } + + // --------------------------------------------------------------------- + + void AppletShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ) + { + try + { + maViewAppletShapes.push_back( + ViewAppletShapeSharedPtr( new ViewAppletShape( rNewLayer, + getXShape(), + maServiceName, + mpPropCopyTable, + mnNumPropEntries, + mxComponentContext ))); + + // push new size to view shape + maViewAppletShapes.back()->resize( getBounds() ); + + // render the Shape on the newly added ViewLayer + if( bRedrawLayer ) + maViewAppletShapes.back()->render( getBounds() ); + } + catch(uno::Exception&) + { + // ignore failed shapes - slideshow should run with + // the remaining content + } + } + + // --------------------------------------------------------------------- + + bool AppletShape::removeViewLayer( const ViewLayerSharedPtr& rLayer ) + { + const ViewAppletShapeVector::iterator aEnd( maViewAppletShapes.end() ); + + OSL_ENSURE( ::std::count_if(maViewAppletShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewAppletShape::getViewLayer, _1 ), + ::boost::cref( rLayer ) ) ) < 2, + "AppletShape::removeViewLayer(): Duplicate ViewLayer entries!" ); + + ViewAppletShapeVector::iterator aIter; + + if( (aIter=::std::remove_if( maViewAppletShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewAppletShape::getViewLayer, + _1 ), + ::boost::cref( rLayer ) ) )) == aEnd ) + { + // view layer seemingly was not added, failed + return false; + } + + // actually erase from container + maViewAppletShapes.erase( aIter, aEnd ); + + return true; + } + + // --------------------------------------------------------------------- + + bool AppletShape::clearAllViewLayers() + { + maViewAppletShapes.clear(); + return true; + } + + // --------------------------------------------------------------------- + + bool AppletShape::implRender( const ::basegfx::B2DRange& rCurrBounds ) const + { + // redraw all view shapes, by calling their update() method + if( ::std::count_if( maViewAppletShapes.begin(), + maViewAppletShapes.end(), + ::boost::bind<bool>( + ::boost::mem_fn( &ViewAppletShape::render ), + _1, + ::boost::cref( rCurrBounds ) ) ) + != static_cast<ViewAppletShapeVector::difference_type>(maViewAppletShapes.size()) ) + { + // at least one of the ViewShape::update() calls did return + // false - update failed on at least one ViewLayer + return false; + } + + return true; + } + + // --------------------------------------------------------------------- + + bool AppletShape::implStartIntrinsicAnimation() + { + ::std::for_each( maViewAppletShapes.begin(), + maViewAppletShapes.end(), + ::boost::bind( &ViewAppletShape::startApplet, + _1, + ::boost::cref( getBounds() ))); + mbIsPlaying = true; + + return true; + } + + // --------------------------------------------------------------------- + + bool AppletShape::implEndIntrinsicAnimation() + { + ::std::for_each( maViewAppletShapes.begin(), + maViewAppletShapes.end(), + ::boost::mem_fn( &ViewAppletShape::endApplet ) ); + + mbIsPlaying = false; + + return true; + } + + // --------------------------------------------------------------------- + + bool AppletShape::implPauseIntrinsicAnimation() + { + // TODO(F1): any way of temporarily disabling/deactivating + // applets? + return true; + } + + // --------------------------------------------------------------------- + + bool AppletShape::implIsIntrinsicAnimationPlaying() const + { + return mbIsPlaying; + } + + // --------------------------------------------------------------------- + + void AppletShape::implSetIntrinsicAnimationTime(double) + { + // No way of doing this, or? + } + + boost::shared_ptr<Shape> createAppletShape( + const uno::Reference< drawing::XShape >& xShape, + double nPrio, + const ::rtl::OUString& rServiceName, + const char** pPropCopyTable, + sal_Size nNumPropEntries, + const SlideShowContext& rContext ) + { + boost::shared_ptr< AppletShape > pAppletShape( + new AppletShape(xShape, + nPrio, + rServiceName, + pPropCopyTable, + nNumPropEntries, + rContext) ); + + return pAppletShape; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/appletshape.hxx b/slideshow/source/engine/shapes/appletshape.hxx new file mode 100644 index 000000000000..e967b9b381fb --- /dev/null +++ b/slideshow/source/engine/shapes/appletshape.hxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_APPLETSHAPE_HXX +#define INCLUDED_SLIDESHOW_APPLETSHAPE_HXX + +#include <com/sun/star/uno/Reference.hxx> +#include <boost/shared_ptr.hpp> + +namespace com { namespace sun { namespace star { namespace drawing +{ + class XShape; +} } } } +namespace rtl { + class OUString; +} + +namespace slideshow +{ + namespace internal + { + struct SlideShowContext; + class Shape; + + boost::shared_ptr<Shape> createAppletShape( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + double nPrio, + const ::rtl::OUString& rServiceName, + const char** pPropCopyTable, + sal_Size nNumPropEntries, + const SlideShowContext& rContext ); + } +} + +#endif /* INCLUDED_SLIDESHOW_APPLETSHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/backgroundshape.cxx b/slideshow/source/engine/shapes/backgroundshape.cxx new file mode 100644 index 000000000000..7d13d9247455 --- /dev/null +++ b/slideshow/source/engine/shapes/backgroundshape.cxx @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <rtl/logfile.hxx> + +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> + +#include <basegfx/numeric/ftools.hxx> + +#include <boost/bind.hpp> + +#include <cmath> // for trigonometry and fabs +#include <algorithm> +#include <functional> +#include <limits> + +#include "backgroundshape.hxx" +#include "slideshowexceptions.hxx" +#include "slideshowcontext.hxx" +#include "gdimtftools.hxx" +#include "shape.hxx" +#include "viewbackgroundshape.hxx" + + +using namespace ::com::sun::star; + + +namespace slideshow +{ + namespace internal + { + /** Representation of a draw document's background shape. + + This class implements the Shape interface for the + background shape. Since the background shape is neither + animatable nor attributable, those more specialized + derivations of the Shape interface are not implemented + here. + + @attention this class is to be treated 'final', i.e. one + should not derive from it. + */ + class BackgroundShape : public Shape + { + public: + /** Create the background shape. + + This method creates a shape that handles the + peculiarities of the draw API regarding background + content. + */ + BackgroundShape( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xDrawPage, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xMasterPage, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape > getXShape() const; + + // View layer methods + //------------------------------------------------------------------ + + virtual void addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ); + virtual bool removeViewLayer( const ViewLayerSharedPtr& rNewLayer ); + virtual bool clearAllViewLayers(); + + + // attribute methods + //------------------------------------------------------------------ + + virtual ::basegfx::B2DRectangle getBounds() const; + virtual ::basegfx::B2DRectangle getDomBounds() const; + virtual ::basegfx::B2DRectangle getUpdateArea() const; + virtual bool isVisible() const; + virtual double getPriority() const; + virtual bool isBackgroundDetached() const; + + + // render methods + //------------------------------------------------------------------ + + virtual bool update() const; + virtual bool render() const; + virtual bool isContentChanged() const; + + private: + /// The metafile actually representing the Shape + GDIMetaFileSharedPtr mpMtf; + + // The attributes of this Shape + ::basegfx::B2DRectangle maBounds; // always needed for rendering + + /// the list of active view shapes (one for each registered view layer) + typedef ::std::vector< ViewBackgroundShapeSharedPtr > ViewBackgroundShapeVector; + ViewBackgroundShapeVector maViewShapes; + }; + + + //////////////////////////////////////////////////////////////////////////////// + + + BackgroundShape::BackgroundShape( const uno::Reference< drawing::XDrawPage >& xDrawPage, + const uno::Reference< drawing::XDrawPage >& xMasterPage, + const SlideShowContext& rContext ) : + mpMtf(), + maBounds(), + maViewShapes() + { + uno::Reference< beans::XPropertySet > xPropSet( xDrawPage, + uno::UNO_QUERY_THROW ); + GDIMetaFileSharedPtr pMtf( new GDIMetaFile() ); + + // first try the page background (overrides + // masterpage background), then try masterpage + if( !getMetaFile( uno::Reference<lang::XComponent>(xDrawPage, uno::UNO_QUERY), + xDrawPage, *pMtf, MTF_LOAD_BACKGROUND_ONLY, + rContext.mxComponentContext ) && + !getMetaFile( uno::Reference<lang::XComponent>(xMasterPage, uno::UNO_QUERY), + xDrawPage, *pMtf, MTF_LOAD_BACKGROUND_ONLY, + rContext.mxComponentContext )) + { + throw ShapeLoadFailedException(); + } + + // there is a special background shape, add it + // as the first one + // --------------------------------------------------- + + sal_Int32 nDocWidth=0; + sal_Int32 nDocHeight=0; + xPropSet->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Width") ) ) >>= nDocWidth; + xPropSet->getPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Height") ) ) >>= nDocHeight; + + mpMtf = pMtf; + maBounds = ::basegfx::B2DRectangle( 0,0,nDocWidth, nDocHeight ); + } + + uno::Reference< drawing::XShape > BackgroundShape::getXShape() const + { + // no real XShape representative + return uno::Reference< drawing::XShape >(); + } + + void BackgroundShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ) + { + ViewBackgroundShapeVector::iterator aEnd( maViewShapes.end() ); + + // already added? + if( ::std::find_if( maViewShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewBackgroundShape::getViewLayer, + _1 ), + ::boost::cref( rNewLayer ) ) ) != aEnd ) + { + // yes, nothing to do + return; + } + + maViewShapes.push_back( + ViewBackgroundShapeSharedPtr( + new ViewBackgroundShape( rNewLayer, + maBounds ) ) ); + + // render the Shape on the newly added ViewLayer + if( bRedrawLayer ) + maViewShapes.back()->render( mpMtf ); + } + + bool BackgroundShape::removeViewLayer( const ViewLayerSharedPtr& rLayer ) + { + const ViewBackgroundShapeVector::iterator aEnd( maViewShapes.end() ); + + OSL_ENSURE( ::std::count_if(maViewShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewBackgroundShape::getViewLayer, + _1 ), + ::boost::cref( rLayer ) ) ) < 2, + "BackgroundShape::removeViewLayer(): Duplicate ViewLayer entries!" ); + + ViewBackgroundShapeVector::iterator aIter; + + if( (aIter=::std::remove_if( maViewShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewBackgroundShape::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 BackgroundShape::clearAllViewLayers() + { + maViewShapes.clear(); + return true; + } + + ::basegfx::B2DRectangle BackgroundShape::getBounds() const + { + return maBounds; + } + + ::basegfx::B2DRectangle BackgroundShape::getDomBounds() const + { + return maBounds; + } + + ::basegfx::B2DRectangle BackgroundShape::getUpdateArea() const + { + // TODO(F1): Need to expand background, too, when + // antialiasing? + + // no transformation etc. possible for background shape + return maBounds; + } + + bool BackgroundShape::isVisible() const + { + return true; + } + + double BackgroundShape::getPriority() const + { + return 0.0; // lowest prio, we're the background + } + + bool BackgroundShape::update() const + { + return render(); + } + + bool BackgroundShape::render() const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::BackgroundShape::render()" ); + RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::presentation::internal::BackgroundShape: 0x%X", this ); + + // gcc again... + const ::basegfx::B2DRectangle& rCurrBounds( BackgroundShape::getBounds() ); + + if( rCurrBounds.getRange().equalZero() ) + { + // zero-sized shapes are effectively invisible, + // thus, we save us the rendering... + return true; + } + + // redraw all view shapes, by calling their render() method + if( ::std::count_if( maViewShapes.begin(), + maViewShapes.end(), + ::boost::bind( &ViewBackgroundShape::render, + _1, + ::boost::cref( mpMtf ) ) ) + != static_cast<ViewBackgroundShapeVector::difference_type>(maViewShapes.size()) ) + { + // at least one of the ViewBackgroundShape::render() calls did return + // false - update failed on at least one ViewLayer + return false; + } + + return true; + } + + bool BackgroundShape::isContentChanged() const + { + return false; + } + + bool BackgroundShape::isBackgroundDetached() const + { + return false; // we're not animatable + } + + ////////////////////////////////////////////////////////// + + ShapeSharedPtr createBackgroundShape( + const uno::Reference< drawing::XDrawPage >& xDrawPage, + const uno::Reference< drawing::XDrawPage >& xMasterPage, + const SlideShowContext& rContext ) + { + return ShapeSharedPtr( + new BackgroundShape( + xDrawPage, + xMasterPage, + rContext )); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/backgroundshape.hxx b/slideshow/source/engine/shapes/backgroundshape.hxx new file mode 100644 index 000000000000..eb495b9b1aec --- /dev/null +++ b/slideshow/source/engine/shapes/backgroundshape.hxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_BACKGROUNDSHAPE_HXX +#define INCLUDED_SLIDESHOW_BACKGROUNDSHAPE_HXX + +#include <com/sun/star/uno/Reference.hxx> +#include <boost/shared_ptr.hpp> + +namespace com { namespace sun { namespace star { namespace drawing +{ + class XDrawPage; +} } } } + +namespace slideshow +{ + namespace internal + { + class Shape; + struct SlideShowContext; + + /** Representation of a draw document's background shape. + + This function generates the Shape for the background + shape. Since the background shape is neither animatable + nor attributable, those more specialized derivations of + the Shape interface are not implemented here. + */ + boost::shared_ptr<Shape> createBackgroundShape( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xDrawPage, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xMasterPage, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + } +} + +#endif /* INCLUDED_SLIDESHOW_BACKGROUNDSHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/drawinglayeranimation.cxx b/slideshow/source/engine/shapes/drawinglayeranimation.cxx new file mode 100644 index 000000000000..a7cb526834bf --- /dev/null +++ b/slideshow/source/engine/shapes/drawinglayeranimation.cxx @@ -0,0 +1,984 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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" + +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <canvas/elapsedtime.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> + +#include <comphelper/anytostring.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <rtl/math.hxx> +#include <vcl/metric.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/metaact.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/TextAnimationKind.hpp> +#include <com/sun/star/drawing/TextAnimationDirection.hpp> +#include <com/sun/star/drawing/TextHorizontalAdjust.hpp> +#include <com/sun/star/drawing/TextVerticalAdjust.hpp> +#include <com/sun/star/drawing/HomogenMatrix3.hpp> +#include <com/sun/star/awt/Rectangle.hpp> + +#include "activity.hxx" +#include "wakeupevent.hxx" +#include "eventqueue.hxx" +#include "drawshapesubsetting.hxx" +#include "drawshape.hxx" +#include "shapesubset.hxx" +#include "shapeattributelayerholder.hxx" +#include "slideshowcontext.hxx" +#include "tools.hxx" +#include "gdimtftools.hxx" +#include "eventmultiplexer.hxx" +#include "intrinsicanimationactivity.hxx" +#include "intrinsicanimationeventhandler.hxx" + +#include <boost/weak_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/noncopyable.hpp> +#include <vector> + +using namespace com::sun::star; +using namespace ::slideshow::internal; + +namespace { + +class ScrollTextAnimNode +{ + sal_uInt32 mnDuration; // single duration + sal_uInt32 mnRepeat; // 0 -> endless + double mfStart; + double mfStop; + sal_uInt32 mnFrequency; // in ms + // forth and back change at mnRepeat%2: + bool mbAlternate; + +public: + ScrollTextAnimNode( + sal_uInt32 nDuration, sal_uInt32 nRepeat, double fStart, double fStop, + sal_uInt32 nFrequency, bool bAlternate) + : mnDuration(nDuration), + mnRepeat(nRepeat), + mfStart(fStart), + mfStop(fStop), + mnFrequency(nFrequency), + mbAlternate(bAlternate) + {} + + sal_uInt32 GetDuration() const { return mnDuration; } + sal_uInt32 GetRepeat() const { return mnRepeat; } + sal_uInt32 GetFullTime() const { return mnDuration * mnRepeat; } + double GetStart() const { return mfStart; } + double GetStop() const { return mfStop; } + sal_uInt32 GetFrequency() const { return mnFrequency; } + bool DoAlternate() const { return mbAlternate; } + + double GetStateAtRelativeTime(sal_uInt32 nRelativeTime) const; +}; + +double ScrollTextAnimNode::GetStateAtRelativeTime( + sal_uInt32 nRelativeTime) const +{ + // #151174# Avoid division by zero. + if( mnDuration == 0 ) + return mfStop; + + if(mnRepeat) + { + // ending + const sal_uInt32 nRepeatCount(nRelativeTime / mnDuration); + sal_uInt32 nFrameTime(nRelativeTime - (nRepeatCount * mnDuration)); + + if(DoAlternate() && (nRepeatCount + 1L) % 2L) + nFrameTime = mnDuration - nFrameTime; + + return mfStart + ((mfStop - mfStart) * + (double(nFrameTime) / mnDuration)); + } + else + { + // endless + sal_uInt32 nFrameTime(nRelativeTime % mnDuration); + + if(DoAlternate()) + { + const sal_uInt32 nRepeatCount(nRelativeTime / mnDuration); + + if((nRepeatCount + 1L) % 2L) + nFrameTime = mnDuration - nFrameTime; + } + + return mfStart + ((mfStop - mfStart) * (double(nFrameTime) / mnDuration)); + } +} + +class ActivityImpl : public Activity, + public boost::enable_shared_from_this<ActivityImpl>, + private boost::noncopyable +{ +public: + virtual ~ActivityImpl(); + + ActivityImpl( + SlideShowContext const& rContext, + boost::shared_ptr<WakeupEvent> const& pWakeupEvent, + boost::shared_ptr<DrawShape> const& pDrawShape ); + + bool enableAnimations(); + + // Disposable: + virtual void dispose(); + // Activity: + virtual double calcTimeLag() const; + virtual bool perform(); + virtual bool isActive() const; + virtual void dequeued(); + virtual void end(); + +private: + void updateShapeAttributes( double fTime, + basegfx::B2DRectangle const& parentBounds ); + + // Access to VisibleWhenSTarted flags + sal_Bool IsVisibleWhenStarted() const { return mbVisibleWhenStarted; } + sal_Bool IsVisibleWhenStopped() const { return mbVisibleWhenStopped; } + + // scroll horizontal? if sal_False, scroll is vertical. + bool ScrollHorizontal() const { + return (drawing::TextAnimationDirection_LEFT == meDirection || + drawing::TextAnimationDirection_RIGHT == meDirection); + } + + // Access to StepWidth in logical units + sal_uInt32 GetStepWidthLogic() const; + + // is the animation direction opposite? + bool DoScrollForward() const { + return (drawing::TextAnimationDirection_RIGHT == meDirection || + drawing::TextAnimationDirection_DOWN == meDirection); + } + + // do alternate text directions? + bool DoAlternate() const { return mbAlternate; } + + // do scroll in? + bool DoScrollIn() const { return mbScrollIn; } + + // Scroll helper methods + void ImpForceScrollTextAnimNodes(); + ScrollTextAnimNode* ImpGetScrollTextAnimNode( + sal_uInt32 nTime, sal_uInt32& rRelativeTime ); + sal_uInt32 ImpRegisterAgainScrollTextMixerState( + sal_uInt32 nTime); + + // calculate the MixerState value for given time + double GetMixerState(sal_uInt32 nTime); + + //////////////////////////////////////////////////////////////////// + + SlideShowContext maContext; + boost::shared_ptr<WakeupEvent> mpWakeupEvent; + boost::weak_ptr<DrawShape> mpParentDrawShape; + DrawShapeSharedPtr mpDrawShape; + ShapeAttributeLayerHolder maShapeAttrLayer; + GDIMetaFileSharedPtr mpMetaFile; + IntrinsicAnimationEventHandlerSharedPtr mpListener; + canvas::tools::ElapsedTime maTimer; + double mfRotationAngle; + bool mbIsShapeAnimated; + bool mbIsDisposed; + bool mbIsActive; + drawing::TextAnimationKind meAnimKind; + + // The blink frequency in ms + sal_uInt32 mnFrequency; + + // The repeat count, init to 0L which means endless + sal_uInt32 mnRepeat; + + // Flag to decide if text will be shown when animation has ended + bool mbVisibleWhenStopped; + bool mbVisibleWhenStarted; + + // Flag decides if TextScroll alternates. Default is sal_False. + bool mbAlternate; + + // Flag to remember if this is a simple scrollin text + bool mbScrollIn; + + // start time for this animation + sal_uInt32 mnStartTime; + + // The AnimationDirection + drawing::TextAnimationDirection meDirection; + + // Get width per Step. Negative means pixel, positive logical units + sal_Int32 mnStepWidth; + + // The single anim steps + std::vector< ScrollTextAnimNode > maVector; + + // the scroll rectangle + Rectangle maScrollRectangleLogic; + + // the paint rectangle + Rectangle maPaintRectangleLogic; +}; + +////////////////////////////////////////////////////////////////////// + +class IntrinsicAnimationListener : public IntrinsicAnimationEventHandler, + private boost::noncopyable +{ +public: + explicit IntrinsicAnimationListener( ActivityImpl& rActivity ) : + mrActivity( rActivity ) + {} + +private: + + virtual bool enableAnimations() { return mrActivity.enableAnimations(); } + virtual bool disableAnimations() { mrActivity.end(); return true; } + + ActivityImpl& mrActivity; +}; + +////////////////////////////////////////////////////////////////////// + +double ActivityImpl::GetMixerState( sal_uInt32 nTime ) +{ + if( meAnimKind == drawing::TextAnimationKind_BLINK ) + { + // from AInfoBlinkText: + double fRetval(0.0); + sal_Bool bDone(sal_False); + const sal_uInt32 nLoopTime(2 * mnFrequency); + + if(mnRepeat) + { + const sal_uInt32 nEndTime(mnRepeat * nLoopTime); + + if(nTime >= nEndTime) + { + if(mbVisibleWhenStopped) + fRetval = 0.0; + else + fRetval = 1.0; + + bDone = sal_True; + } + } + + if(!bDone) + { + sal_uInt32 nTimeInLoop(nTime % nLoopTime); + fRetval = double(nTimeInLoop) / nLoopTime; + } + + return fRetval; + } + else + { + // from AInfoScrollText: + double fRetval(0.0); + ImpForceScrollTextAnimNodes(); + + if(!maVector.empty()) + { + sal_uInt32 nRelativeTime; + ScrollTextAnimNode* pNode = + ImpGetScrollTextAnimNode(nTime, nRelativeTime); + + if(pNode) + { + // use node + fRetval = pNode->GetStateAtRelativeTime(nRelativeTime); + } + else + { + // end of animation, take last entry's end + fRetval = maVector[maVector.size() - 1L].GetStop(); + } + } + + return fRetval; + } +} + +// Access to StepWidth in logical units +sal_uInt32 ActivityImpl::GetStepWidthLogic() const +{ + // #i69847# Assuming higher DPI + sal_uInt32 const PIXEL_TO_LOGIC = 30; + + sal_uInt32 nRetval(0L); + + if(mnStepWidth < 0L) + { + // is in pixels, convert to logical units + nRetval = (-mnStepWidth * PIXEL_TO_LOGIC); + } + else if(mnStepWidth > 0L) + { + // is in logical units + nRetval = mnStepWidth; + } + + if(0L == nRetval) + { + // step 1 pixel, canned value + + // #128389# with very high DPIs like in PDF export, this can + // still get zero. for that cases, set a default, too (taken + // from ainfoscrolltext.cxx) + nRetval = 100L; + } + + return nRetval; +} + +void ActivityImpl::ImpForceScrollTextAnimNodes() +{ + if(maVector.empty()) + { + // prepare values + sal_uInt32 nLoopTime; + double fZeroLogic, fOneLogic, fInitLogic, fDistanceLogic; + double fZeroLogicAlternate = 0.0, fOneLogicAlternate = 0.0; + double fZeroRelative, fOneRelative, fInitRelative; + + if(ScrollHorizontal()) + { + if(DoAlternate()) + { + if(maPaintRectangleLogic.GetWidth() > + maScrollRectangleLogic.GetWidth()) + { + fZeroLogicAlternate = maScrollRectangleLogic.Right() - maPaintRectangleLogic.GetWidth(); + fOneLogicAlternate = maScrollRectangleLogic.Left(); + } + else + { + fZeroLogicAlternate = maScrollRectangleLogic.Left(); + fOneLogicAlternate = maScrollRectangleLogic.Right() - maPaintRectangleLogic.GetWidth(); + } + } + + fZeroLogic = maScrollRectangleLogic.Left() - maPaintRectangleLogic.GetWidth(); + fOneLogic = maScrollRectangleLogic.Right(); + fInitLogic = maPaintRectangleLogic.Left(); + } + else + { + if(DoAlternate()) + { + if(maPaintRectangleLogic.GetHeight() > maScrollRectangleLogic.GetHeight()) + { + fZeroLogicAlternate = maScrollRectangleLogic.Bottom() - maPaintRectangleLogic.GetHeight(); + fOneLogicAlternate = maScrollRectangleLogic.Top(); + } + else + { + fZeroLogicAlternate = maScrollRectangleLogic.Top(); + fOneLogicAlternate = maScrollRectangleLogic.Bottom() - maPaintRectangleLogic.GetHeight(); + } + } + + fZeroLogic = maScrollRectangleLogic.Top() - maPaintRectangleLogic.GetHeight(); + fOneLogic = maScrollRectangleLogic.Bottom(); + fInitLogic = maPaintRectangleLogic.Top(); + } + + fDistanceLogic = fOneLogic - fZeroLogic; + fInitRelative = (fInitLogic - fZeroLogic) / fDistanceLogic; + + if(DoAlternate()) + { + fZeroRelative = + (fZeroLogicAlternate - fZeroLogic) / fDistanceLogic; + fOneRelative = + (fOneLogicAlternate - fZeroLogic) / fDistanceLogic; + } + else + { + fZeroRelative = 0.0; + fOneRelative = 1.0; + } + + if(mnStartTime) + { + // Start time loop + ScrollTextAnimNode aStartNode( + mnStartTime, 1L, 0.0, 0.0, mnStartTime, false); + maVector.push_back(aStartNode); + } + + if(IsVisibleWhenStarted()) + { + double fRelativeStartValue, fRelativeEndValue,fRelativeDistance; + + if(DoScrollForward()) + { + fRelativeStartValue = fInitRelative; + fRelativeEndValue = fOneRelative; + fRelativeDistance = fRelativeEndValue - fRelativeStartValue; + } + else + { + fRelativeStartValue = fInitRelative; + fRelativeEndValue = fZeroRelative; + fRelativeDistance = fRelativeStartValue - fRelativeEndValue; + } + + const double fNumberSteps = + (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); + nLoopTime = FRound(fNumberSteps * mnFrequency); + + // init loop + ScrollTextAnimNode aInitNode( + nLoopTime, 1L, + fRelativeStartValue, fRelativeEndValue, + mnFrequency, false); + maVector.push_back(aInitNode); + } + + // prepare main loop values + { + double fRelativeStartValue, fRelativeEndValue, fRelativeDistance; + + if(DoScrollForward()) + { + fRelativeStartValue = fZeroRelative; + fRelativeEndValue = fOneRelative; + fRelativeDistance = fRelativeEndValue - fRelativeStartValue; + } + else + { + fRelativeStartValue = fOneRelative; + fRelativeEndValue = fZeroRelative; + fRelativeDistance = fRelativeStartValue - fRelativeEndValue; + } + + const double fNumberSteps = + (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); + nLoopTime = FRound(fNumberSteps * mnFrequency); + + if(0L == mnRepeat) + { + if(!DoScrollIn()) + { + // endless main loop + ScrollTextAnimNode aMainNode( + nLoopTime, 0L, + fRelativeStartValue, fRelativeEndValue, + mnFrequency, DoAlternate()); + maVector.push_back(aMainNode); + } + } + else + { + sal_uInt32 nNumRepeat(mnRepeat); + + if(DoAlternate() && (nNumRepeat + 1L) % 2L) + nNumRepeat += 1L; + + // ending main loop + ScrollTextAnimNode aMainNode( + nLoopTime, nNumRepeat, + fRelativeStartValue, fRelativeEndValue, + mnFrequency, DoAlternate()); + maVector.push_back(aMainNode); + } + } + + if(IsVisibleWhenStopped()) + { + double fRelativeStartValue, fRelativeEndValue, fRelativeDistance; + + if(DoScrollForward()) + { + fRelativeStartValue = fZeroRelative; + fRelativeEndValue = fInitRelative; + fRelativeDistance = fRelativeEndValue - fRelativeStartValue; + } + else + { + fRelativeStartValue = fOneRelative; + fRelativeEndValue = fInitRelative; + fRelativeDistance = fRelativeStartValue - fRelativeEndValue; + } + + const double fNumberSteps = + (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); + nLoopTime = FRound(fNumberSteps * mnFrequency); + + // exit loop + ScrollTextAnimNode aExitNode( + nLoopTime, 1L, + fRelativeStartValue, fRelativeEndValue, mnFrequency, false); + maVector.push_back(aExitNode); + } + } +} + +ScrollTextAnimNode* ActivityImpl::ImpGetScrollTextAnimNode( + sal_uInt32 nTime, sal_uInt32& rRelativeTime ) +{ + ScrollTextAnimNode* pRetval = 0L; + ImpForceScrollTextAnimNodes(); + + if(!maVector.empty()) + { + rRelativeTime = nTime; + + for(sal_uInt32 a(0L); !pRetval && a < maVector.size(); a++) + { + ScrollTextAnimNode & rNode = maVector[a]; + if(!rNode.GetRepeat()) + { + // endless loop, use it + pRetval = &rNode; + } + else if(rNode.GetFullTime() > rRelativeTime) + { + // ending node + pRetval = &rNode; + } + else + { + // look at next + rRelativeTime -= rNode.GetFullTime(); + } + } + } + + return pRetval; +} + +sal_uInt32 ActivityImpl::ImpRegisterAgainScrollTextMixerState(sal_uInt32 nTime) +{ + sal_uInt32 nRetval(0L); + ImpForceScrollTextAnimNodes(); + + if(maVector.size()) + { + sal_uInt32 nRelativeTime; + ScrollTextAnimNode* pNode = ImpGetScrollTextAnimNode(nTime, nRelativeTime); + + if(pNode) + { + // take register time + nRetval = pNode->GetFrequency(); + } + } + else + { + // #i38135# not initialized, return default + nRetval = mnFrequency; + } + + return nRetval; +} + +void ActivityImpl::updateShapeAttributes( + double fTime, basegfx::B2DRectangle const& parentBounds ) +{ + OSL_ASSERT( meAnimKind != drawing::TextAnimationKind_NONE ); + if( meAnimKind == drawing::TextAnimationKind_NONE ) + return; + + double const fMixerState = GetMixerState( + static_cast<sal_uInt32>(fTime * 1000.0) ); + + if( meAnimKind == drawing::TextAnimationKind_BLINK ) + { + // show/hide text: + maShapeAttrLayer.get()->setVisibility( fMixerState < 0.5 ); + } + else if(mpMetaFile) // scroll mode: + { + // + // keep care: the below code is highly sensible to changes... + // + + // rectangle of the pure text: + double const fPaintWidth = maPaintRectangleLogic.GetWidth(); + double const fPaintHeight = maPaintRectangleLogic.GetHeight(); + // rectangle where the scrolling takes place (-> clipping): + double const fScrollWidth = maScrollRectangleLogic.GetWidth(); + double const fScrollHeight = maScrollRectangleLogic.GetHeight(); + + basegfx::B2DPoint pos, clipPos; + + if(ScrollHorizontal()) + { + double const fOneEquiv( fScrollWidth ); + double const fZeroEquiv( -fPaintWidth ); + + pos.setX( fZeroEquiv + (fMixerState * (fOneEquiv - fZeroEquiv)) ); + + clipPos.setX( -pos.getX() ); + clipPos.setY( -pos.getY() ); + + // #i69844# Compensation for text-wider-than-shape case + if( fPaintWidth > fScrollWidth ) + pos.setX( pos.getX() + (fPaintWidth-fScrollWidth) / 2.0 ); + } + else + { + // scroll vertical: + double const fOneEquiv( fScrollHeight ); + double const fZeroEquiv( -fPaintHeight ); + + pos.setY( fZeroEquiv + (fMixerState * (fOneEquiv - fZeroEquiv)) ); + + clipPos.setX( -pos.getX() ); + clipPos.setY( -pos.getY() ); + + // #i69844# Compensation for text-higher-than-shape case + if( fPaintHeight > fScrollHeight ) + pos.setY( pos.getY() + (fPaintHeight-fScrollHeight) / 2.0 ); + } + + basegfx::B2DPolygon clipPoly( + basegfx::tools::createPolygonFromRect( + basegfx::B2DRectangle( clipPos.getX(), + clipPos.getY(), + clipPos.getX() + fScrollWidth, + clipPos.getY() + fScrollHeight ) ) ); + + if( !::basegfx::fTools::equalZero( mfRotationAngle )) + { + maShapeAttrLayer.get()->setRotationAngle( mfRotationAngle ); + double const fRotate = (mfRotationAngle * M_PI / 180.0); + basegfx::B2DHomMatrix aTransform; + // position: + aTransform.rotate( fRotate ); + pos *= aTransform; + } + + pos += parentBounds.getCenter(); + maShapeAttrLayer.get()->setPosition( pos ); + maShapeAttrLayer.get()->setClip( basegfx::B2DPolyPolygon(clipPoly) ); + } +} + +bool ActivityImpl::perform() +{ + if( !isActive() ) + return false; + + ENSURE_OR_RETURN_FALSE( + mpDrawShape, + "ActivityImpl::perform(): still active, but NULL draw shape" ); + + DrawShapeSharedPtr const pParentDrawShape( mpParentDrawShape ); + if( !pParentDrawShape ) + return false; // parent has vanished + + if( pParentDrawShape->isVisible() ) + { + if( !mbIsShapeAnimated ) + { + mpDrawShape->setVisibility(true); // shape may be initially hidden + maContext.mpSubsettableShapeManager->enterAnimationMode( mpDrawShape ); + maTimer.reset(); + mbIsShapeAnimated = true; + } + // update attributes related to current time: + basegfx::B2DRectangle const parentBounds( + pParentDrawShape->getBounds() ); + + const double nCurrTime( maTimer.getElapsedTime() ); + updateShapeAttributes( nCurrTime, parentBounds ); + + const sal_uInt32 nFrequency( + ImpRegisterAgainScrollTextMixerState( + static_cast<sal_uInt32>(nCurrTime * 1000.0)) ); + + if(nFrequency) + { + mpWakeupEvent->start(); + mpWakeupEvent->setNextTimeout( + std::max(0.1,nFrequency/1000.0) ); + maContext.mrEventQueue.addEvent( mpWakeupEvent ); + + if( mpDrawShape->isContentChanged() ) + maContext.mpSubsettableShapeManager->notifyShapeUpdate( mpDrawShape ); + } + // else: finished, not need to wake up again. + } + else + { + // busy-wait, until parent shape gets visible + mpWakeupEvent->start(); + mpWakeupEvent->setNextTimeout( 2.0 ); + } + + // don't reinsert, WakeupEvent will perform that after the given timeout: + return false; +} + +ActivityImpl::ActivityImpl( + SlideShowContext const& rContext, + boost::shared_ptr<WakeupEvent> const& pWakeupEvent, + boost::shared_ptr<DrawShape> const& pParentDrawShape ) + : maContext(rContext), + mpWakeupEvent(pWakeupEvent), + mpParentDrawShape(pParentDrawShape), + mpListener( new IntrinsicAnimationListener(*this) ), + maTimer(rContext.mrEventQueue.getTimer()), + mbIsShapeAnimated(false), + mbIsDisposed(false), + mbIsActive(true), + meAnimKind(drawing::TextAnimationKind_NONE), + mnStartTime(0L) +{ + // get doctreenode: + sal_Int32 const nNodes = pParentDrawShape->getNumberOfTreeNodes( + DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ); + + DocTreeNode scrollTextNode( + pParentDrawShape->getTreeNode( + 0, DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH )); + // xxx todo: remove this hack + if( nNodes > 1 ) + scrollTextNode.setEndIndex( + pParentDrawShape->getTreeNode( + nNodes - 1, + DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ).getEndIndex()); + + // TODO(Q3): Doing this manually, instead of using + // ShapeSubset. This is because of lifetime issues (ShapeSubset + // generates circular references to parent shape) + mpDrawShape = boost::dynamic_pointer_cast<DrawShape>( + maContext.mpSubsettableShapeManager->getSubsetShape( + pParentDrawShape, + scrollTextNode )); + + mpMetaFile = mpDrawShape->forceScrollTextMetaFile(); + + // make scroll text invisible for slide transition bitmaps + mpDrawShape->setVisibility(false); + + basegfx::B2DRectangle aScrollRect, aPaintRect; + ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect, + aPaintRect, + mpMetaFile ), + "ActivityImpl::ActivityImpl(): Could not extract " + "scroll anim rectangles from mtf" ); + + maScrollRectangleLogic = vcl::unotools::rectangleFromB2DRectangle( + aScrollRect ); + maPaintRectangleLogic = vcl::unotools::rectangleFromB2DRectangle( + aPaintRect ); + + maShapeAttrLayer.createAttributeLayer(mpDrawShape); + + uno::Reference<drawing::XShape> const xShape( mpDrawShape->getXShape() ); + uno::Reference<beans::XPropertySet> const xProps( xShape, uno::UNO_QUERY_THROW ); + + getPropertyValue( meAnimKind, xProps, OUSTR("TextAnimationKind") ); + OSL_ASSERT( meAnimKind != drawing::TextAnimationKind_NONE ); + mbAlternate = (meAnimKind == drawing::TextAnimationKind_ALTERNATE); + mbScrollIn = (meAnimKind == drawing::TextAnimationKind_SLIDE); + + // adopted from in AInfoBlinkText::ImplInit(): + sal_Int16 nRepeat(0); + getPropertyValue( nRepeat, xProps, OUSTR("TextAnimationCount") ); + mnRepeat = nRepeat; + + if(mbAlternate) + { + // force visible when started for scroll-forth-and-back, because + // slide has been coming in with visible text in the middle: + mbVisibleWhenStarted = true; + } + else + { + getPropertyValue( mbVisibleWhenStarted, xProps, + OUSTR("TextAnimationStartInside") ); + } + + // set visible when stopped + getPropertyValue( mbVisibleWhenStopped, xProps, + OUSTR("TextAnimatiogonStopInside") ); + // rotation: + getPropertyValue( mfRotationAngle, xProps, + OUSTR("RotateAngle") ); + mfRotationAngle /= -100.0; // (switching direction) + + // set frequency + sal_Int16 nDelay(0); + getPropertyValue( nDelay, xProps, OUSTR("TextAnimationDelay") ); + // set delay if not automatic + mnFrequency = (nDelay ? nDelay : + // default: + meAnimKind == drawing::TextAnimationKind_BLINK + ? 250L : 50L ); + + // adopted from in AInfoScrollText::ImplInit(): + + // If it is a simple m_bScrollIn, reset some parameters + if( DoScrollIn() ) + { + // most parameters are set correctly from the dialog logic, but + // eg VisisbleWhenStopped is grayed out and needs to be corrected here. + mbVisibleWhenStopped = true; + mbVisibleWhenStarted = false; + mnRepeat = 0L; + } + + // Get animation direction + getPropertyValue( meDirection, xProps, OUSTR("TextAnimationDirection") ); + + // Get step width. Negative means pixel, positive logical units + getPropertyValue( mnStepWidth, xProps, OUSTR("TextAnimationAmount") ); + + maContext.mpSubsettableShapeManager->addIntrinsicAnimationHandler( + mpListener ); +} + +bool ActivityImpl::enableAnimations() +{ + mbIsActive = true; + return maContext.mrActivitiesQueue.addActivity( + shared_from_this() ); +} + +ActivityImpl::~ActivityImpl() +{ +} + +void ActivityImpl::dispose() +{ + if( !mbIsDisposed ) + { + end(); + + // only remove subset here, since end() is called on slide end + // (and we must not spoil the slide preview bitmap with scroll + // text) + maShapeAttrLayer.reset(); + if( mpDrawShape ) + { + // TODO(Q3): Doing this manually, instead of using + // ShapeSubset. This is because of lifetime issues + // (ShapeSubset generates circular references to parent + // shape) + DrawShapeSharedPtr pParent( mpParentDrawShape.lock() ); + if( pParent ) + maContext.mpSubsettableShapeManager->revokeSubset( + pParent, + mpDrawShape ); + } + + mpMetaFile.reset(); + mpDrawShape.reset(); + mpParentDrawShape.reset(); + mpWakeupEvent.reset(); + maContext.dispose(); + mbIsDisposed = true; + + maContext.mpSubsettableShapeManager->removeIntrinsicAnimationHandler( + mpListener ); + } +} + +double ActivityImpl::calcTimeLag() const +{ + return 0.0; +} + +bool ActivityImpl::isActive() const +{ + return mbIsActive; +} + +void ActivityImpl::dequeued() +{ + // not used here +} + +void ActivityImpl::end() +{ + // not used here + mbIsActive = false; + + if( mbIsShapeAnimated ) + { + maContext.mpSubsettableShapeManager->leaveAnimationMode( mpDrawShape ); + mbIsShapeAnimated = false; + } +} + +} // anon namespace + +namespace slideshow { +namespace internal { + +boost::shared_ptr<Activity> createDrawingLayerAnimActivity( + SlideShowContext const& rContext, + boost::shared_ptr<DrawShape> const& pDrawShape ) +{ + boost::shared_ptr<Activity> pActivity; + + try + { + boost::shared_ptr<WakeupEvent> const pWakeupEvent( + new WakeupEvent( rContext.mrEventQueue.getTimer(), + rContext.mrActivitiesQueue ) ); + pActivity.reset( new ActivityImpl( rContext, pWakeupEvent, pDrawShape ) ); + pWakeupEvent->setActivity( pActivity ); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( uno::Exception& ) + { + // translate any error into empty factory product. + OSL_ENSURE( false, + rtl::OUStringToOString( + comphelper::anyToString( cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + + return pActivity; +} + +} // namespace internal +} // namespace presentation + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/drawinglayeranimation.hxx b/slideshow/source/engine/shapes/drawinglayeranimation.hxx new file mode 100644 index 000000000000..45d9afe91f03 --- /dev/null +++ b/slideshow/source/engine/shapes/drawinglayeranimation.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ +#ifndef INCLUDED_DRAWINGLAYERANIMATION_HXX +#define INCLUDED_DRAWINGLAYERANIMATION_HXX + +#include <sal/config.h> +#include <boost/shared_ptr.hpp> + +namespace slideshow { +namespace internal { + +class Activity; +struct SlideShowContext; +class DrawShape; + +boost::shared_ptr<Activity> createDrawingLayerAnimActivity( + SlideShowContext const& rContext, + boost::shared_ptr<DrawShape> const& pDrawShape ); + +} // namespace internal +} // namespace presentation + +#endif // ! defined INCLUDED_DRAWINGLAYERANIMATION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/drawshape.cxx b/slideshow/source/engine/shapes/drawshape.cxx new file mode 100644 index 000000000000..e018c8015b0a --- /dev/null +++ b/slideshow/source/engine/shapes/drawshape.cxx @@ -0,0 +1,1481 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/drawshape.hxx b/slideshow/source/engine/shapes/drawshape.hxx new file mode 100644 index 000000000000..a657d337064e --- /dev/null +++ b/slideshow/source/engine/shapes/drawshape.hxx @@ -0,0 +1,385 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_DRAWSHAPE_HXX +#define INCLUDED_SLIDESHOW_DRAWSHAPE_HXX + +#include <osl/diagnose.hxx> +#include <com/sun/star/drawing/XShape.hpp> + +#include "attributableshape.hxx" +#include "doctreenodesupplier.hxx" +#include "gdimtftools.hxx" +#include "viewshape.hxx" +#include "hyperlinkarea.hxx" + +#include <boost/optional.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <set> +#include <vector> + +class Graphic; + +namespace slideshow +{ + namespace internal + { + class Activity; + struct SlideShowContext; + class DrawShapeSubsetting; + class DrawShape; + typedef ::boost::shared_ptr< DrawShape > DrawShapeSharedPtr; + + /** This class is the representation of a draw document's + XShape, and implements the Shape, AnimatableShape, and + AttributableShape interfaces. + + @attention this class is to be treated 'final', i.e. one + should not derive from it. + */ + class DrawShape : public AttributableShape, + public DocTreeNodeSupplier, + public HyperlinkArea, + protected ::osl::DebugBase<DrawShape> + { + public: + /** Create a shape for the given XShape + + @param xShape + The XShape to represent. + + @param xContainingPage + The page that contains this shape. Needed for proper + import (currently, the UnoGraphicExporter needs this + information). + + @param nPrio + Externally-determined shape priority (used e.g. for + paint ordering). This number _must be_ unique! + + @param bForeignSource + When true, the source of the shape metafile might be a + foreign application. The metafile is checked against + unsupported content, and, if necessary, returned as a + pre-rendererd bitmap. + */ + static DrawShapeSharedPtr create( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xContainingPage, + double nPrio, + bool bForeignSource, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + + /** Create a shape for the given XShape and graphic content + + @param xShape + The XShape to represent. + + @param xContainingPage + The page that contains this shape. Needed for proper + import (currently, the UnoGraphicExporter needs this + information). + + @param nPrio + Externally-determined shape priority (used e.g. for + paint ordering). This number _must be_ unique! + + @param rGraphic + Graphic to display in the shape's bound rect. If this + Graphic contains animatable content, the created + DrawShape will register itself for intrinsic animation + events. + */ + static DrawShapeSharedPtr create( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xContainingPage, + double nPrio, + const Graphic& rGraphic, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape > getXShape() const; + + virtual ~DrawShape(); + + + // View layer methods + //------------------------------------------------------------------ + + virtual void addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ); + virtual bool removeViewLayer( const ViewLayerSharedPtr& rNewLayer ); + virtual bool clearAllViewLayers(); + + // attribute methods + //------------------------------------------------------------------ + + virtual ShapeAttributeLayerSharedPtr createAttributeLayer(); + virtual bool revokeAttributeLayer( const ShapeAttributeLayerSharedPtr& rLayer ); + virtual ShapeAttributeLayerSharedPtr getTopmostAttributeLayer() const; + virtual void setVisibility( bool bVisible ); + virtual ::basegfx::B2DRectangle getBounds() const; + virtual ::basegfx::B2DRectangle getDomBounds() const; + virtual ::basegfx::B2DRectangle getUpdateArea() const; + virtual bool isVisible() const; + virtual double getPriority() const; + + + // animation methods + //------------------------------------------------------------------ + + virtual void enterAnimationMode(); + virtual void leaveAnimationMode(); + virtual bool isBackgroundDetached() const; + + // render methods + //------------------------------------------------------------------ + + virtual bool update() const; + virtual bool render() const; + virtual bool isContentChanged() const; + + // Sub item specialities + //------------------------------------------------------------------ + + virtual const DocTreeNodeSupplier& getTreeNodeSupplier() const; + virtual DocTreeNodeSupplier& getTreeNodeSupplier(); + + virtual DocTreeNode getSubsetNode() const; + virtual AttributableShapeSharedPtr getSubset( const DocTreeNode& rTreeNode ) const; + virtual bool createSubset( AttributableShapeSharedPtr& o_rSubset, + const DocTreeNode& rTreeNode ); + virtual bool revokeSubset( const AttributableShapeSharedPtr& rShape ); + + + // DocTreeNodeSupplier methods + //------------------------------------------------------------------ + + virtual sal_Int32 getNumberOfTreeNodes ( DocTreeNode::NodeType eNodeType ) const; // throw ShapeLoadFailedException; + virtual DocTreeNode getTreeNode ( sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const; // throw ShapeLoadFailedException; + virtual sal_Int32 getNumberOfSubsetTreeNodes ( const DocTreeNode& rParentNode, + DocTreeNode::NodeType eNodeType ) const; // throw ShapeLoadFailedException; + virtual DocTreeNode getSubsetTreeNode ( const DocTreeNode& rParentNode, + sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const; // throw ShapeLoadFailedException; + + // HyperlinkArea methods + //------------------------------------------------------------------ + + virtual HyperlinkRegions getHyperlinkRegions() const; + virtual double getHyperlinkPriority() const; + + + // intrinsic animation methods + //------------------------------------------------------------------ + + /** Display next frame of an intrinsic animation. + + Used by IntrinsicAnimationActivity, to show the next + animation frame. + */ + bool setIntrinsicAnimationFrame( ::std::size_t nCurrFrame ); + + /** forces the drawshape to load and return a specially + crafted metafile, usable to display drawing layer text + animations. + */ + GDIMetaFileSharedPtr forceScrollTextMetaFile(); + + private: + /** Create a shape for the given XShape + + @param xShape + The XShape to represent. + + @param xContainingPage + The page that contains this shape. Needed for proper + import (currently, the UnoGraphicExporter needs this + information). + + @param nPrio + Externally-determined shape priority (used e.g. for + paint ordering). This number _must be_ unique! + + @param bForeignSource + When true, the source of the shape metafile might be a + foreign application. The metafile is checked against + unsupported content, and, if necessary, returned as a + pre-rendererd bitmap. + */ + DrawShape( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xContainingPage, + double nPrio, + bool bForeignSource, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + + /** Create a shape for the given XShape and graphic content + + @param xShape + The XShape to represent. + + @param xContainingPage + The page that contains this shape. Needed for proper + import (currently, the UnoGraphicExporter needs this + information). + + @param nPrio + Externally-determined shape priority (used e.g. for + paint ordering). This number _must be_ unique! + + @param rGraphic + Graphic to display in the shape's bound rect. If this + Graphic contains animatable content, the created + DrawShape will register itself for intrinsic animation + events. + */ + DrawShape( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage >& xContainingPage, + double nPrio, + const Graphic& rGraphic, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + + /** Private copy constructor + + Used to create subsetted shapes + */ + DrawShape( const DrawShape&, const DocTreeNode& rTreeNode, double nPrio ); + + int getUpdateFlags() const; + bool implRender( int nUpdateFlags ) const; + void updateStateIds() const; + + ViewShape::RenderArgs getViewRenderArgs() const; + ::basegfx::B2DRectangle getActualUnitShapeBounds() const; + + void ensureVerboseMtfComments() const; + bool hasIntrinsicAnimation() const; + bool hasHyperlinks() const; + void prepareHyperlinkIndices() const; + + /// The associated XShape + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape > mxShape; + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XDrawPage > mxPage; + + /** A vector of metafiles actually representing the Shape. + + If this shape is not animated, only a single entry is + available. + */ + mutable VectorOfMtfAnimationFrames maAnimationFrames; + ::std::size_t mnCurrFrame; + + /// Metafile of currently active frame (static for shapes w/o intrinsic animation) + mutable GDIMetaFileSharedPtr mpCurrMtf; + + /// loadflags of current meta file + mutable int mnCurrMtfLoadFlags; + + /// Contains the current shape bounds, in unit rect space + mutable ::boost::optional<basegfx::B2DRectangle> maCurrentShapeUnitBounds; + + // The attributes of this Shape + const double mnPriority; + ::basegfx::B2DRectangle maBounds; // always needed for rendering. + // for subset shapes, this member + // might change when views are + // added, as minimal bounds are + // calculated + + // Pointer to modifiable shape attributes + ShapeAttributeLayerSharedPtr mpAttributeLayer; // only created lazily + + // held here, to signal our destruction + boost::weak_ptr<Activity> mpIntrinsicAnimationActivity; + + // The attribute states, to detect attribute changes, + // without buffering and querying each single attribute + mutable State::StateId mnAttributeTransformationState; + mutable State::StateId mnAttributeClipState; + mutable State::StateId mnAttributeAlphaState; + mutable State::StateId mnAttributePositionState; + mutable State::StateId mnAttributeContentState; + mutable State::StateId mnAttributeVisibilityState; + + /// the list of active view shapes (one for each registered view layer) + typedef ::std::vector< ViewShapeSharedPtr > ViewShapeVector; + ViewShapeVector maViewShapes; + + ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XComponentContext> mxComponentContext; + + /// hyperlink support + typedef ::std::pair<sal_Int32 /* mtf start */, + sal_Int32 /* mtf end */> HyperlinkIndexPair; + typedef ::std::vector<HyperlinkIndexPair> HyperlinkIndexPairVector; + mutable HyperlinkIndexPairVector maHyperlinkIndices; + mutable HyperlinkRegions maHyperlinkRegions; + + /// Delegated subset handling + mutable DrawShapeSubsetting maSubsetting; + + /// Whether this shape is currently in animation mode (value != 0) + int mnIsAnimatedCount; + + /// Number of times the bitmap animation shall loop + ::std::size_t mnAnimationLoopCount; + + /// Cycle mode for bitmap animation + CycleMode meCycleMode; + + /// Whether shape is visible (without attribute layers) + bool mbIsVisible; + + /// Whether redraw is necessary, regardless of state ids + mutable bool mbForceUpdate; + + /// Whether attribute layer was revoked (making a redraw necessary) + mutable bool mbAttributeLayerRevoked; + + /// whether a drawing layer animation has to be performed + bool mbDrawingLayerAnim; + + }; + } +} + +#endif /* INCLUDED_SLIDESHOW_DRAWSHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/drawshapesubsetting.cxx b/slideshow/source/engine/shapes/drawshapesubsetting.cxx new file mode 100644 index 000000000000..62dc22ea3dc0 --- /dev/null +++ b/slideshow/source/engine/shapes/drawshapesubsetting.cxx @@ -0,0 +1,868 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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/math.hxx> +#include <rtl/logfile.hxx> + +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <basegfx/numeric/ftools.hxx> + +#include "drawshapesubsetting.hxx" +#include "drawshape.hxx" + +#include <boost/bind.hpp> + +#include <algorithm> +#include <functional> +#include <limits> + +using namespace ::com::sun::star; + + +namespace slideshow +{ + namespace internal + { + + ////////////////////////////////////////////////////////////////////// + // + // Private methods + // + ////////////////////////////////////////////////////////////////////// + + void DrawShapeSubsetting::ensureInitializedNodeTree() const + { + ENSURE_OR_THROW( mpMtf, + "DrawShapeSubsetting::ensureInitializedNodeTree(): Invalid mtf" ); + + if( mbNodeTreeInitialized ) + return; // done, already initialized. + + // init doctree vector + maActionClassVector.clear(); + maActionClassVector.reserve( mpMtf->GetActionCount() ); + + // search metafile for text output + MetaAction* pCurrAct; + + sal_Int32 nActionIndex(0); + sal_Int32 nLastTextActionIndex(0); + for( pCurrAct = mpMtf->FirstAction(); pCurrAct; pCurrAct = mpMtf->NextAction() ) + { + // check for one of our special text doctree comments + switch( pCurrAct->GetType() ) + { + case META_COMMENT_ACTION: + { + MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct); + + // skip comment if not a special XTEXT comment + if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT", 5 ) == COMPARE_EQUAL ) + { + // fill classification vector with NOOPs, + // then insert corresponding classes at + // the given index + maActionClassVector.resize( nActionIndex+1, CLASS_NOOP ); + + if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOC" ) == COMPARE_EQUAL ) + { + // special, because can happen + // in-between of portions - set + // character-end classificator at + // given index (relative to last text + // action). + const sal_Int32 nIndex( nLastTextActionIndex + pAct->GetValue() ); + + ENSURE_OR_THROW( static_cast< ::std::size_t >(nIndex) < maActionClassVector.size(), + "DrawShapeSubsetting::ensureInitializedNodeTree(): sentence index out of range" ); + + maActionClassVector[ nIndex ] = CLASS_CHARACTER_CELL_END; + } + else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOW" ) == COMPARE_EQUAL ) + { + // special, because can happen + // in-between of portions - set + // word-end classificator at given + // index (relative to last text + // action). + const sal_Int32 nIndex( nLastTextActionIndex + pAct->GetValue() ); + + ENSURE_OR_THROW( static_cast< ::std::size_t >(nIndex) < maActionClassVector.size(), + "DrawShapeSubsetting::ensureInitializedNodeTree(): sentence index out of range" ); + + maActionClassVector[ nIndex ] = CLASS_WORD_END; + } + else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOS" ) == COMPARE_EQUAL ) + { + // special, because can happen + // in-between of portions - set + // sentence-end classificator at given + // index (relative to last text + // action). + const sal_Int32 nIndex( nLastTextActionIndex + pAct->GetValue() ); + + ENSURE_OR_THROW( static_cast< ::std::size_t >(nIndex) < maActionClassVector.size(), + "DrawShapeSubsetting::ensureInitializedNodeTree(): sentence index out of range" ); + + maActionClassVector[ nIndex ] = CLASS_SENTENCE_END; + } + else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOL" ) == COMPARE_EQUAL ) + { + maActionClassVector[ nActionIndex ] = CLASS_LINE_END; + } + else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOP" ) == COMPARE_EQUAL ) + { + maActionClassVector[ nActionIndex ] = CLASS_PARAGRAPH_END; + } + else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_PAINTSHAPE_END" ) == COMPARE_EQUAL ) + { + maActionClassVector[ nActionIndex ] = CLASS_SHAPE_END; + } + else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_PAINTSHAPE_BEGIN" ) == COMPARE_EQUAL ) + { + maActionClassVector[ nActionIndex ] = CLASS_SHAPE_START; + } + } + ++nActionIndex; + break; + } + case META_TEXT_ACTION: + case META_TEXTARRAY_ACTION: + case META_STRETCHTEXT_ACTION: + nLastTextActionIndex = nActionIndex; + // fallthrough intended + default: + // comment action and all actions not + // explicitely handled here: + nActionIndex += getNextActionOffset(pCurrAct); + break; + } + } + + mbNodeTreeInitialized = true; + } + + void DrawShapeSubsetting::updateSubsetBounds( const SubsetEntry& rSubsetEntry ) + { + // TODO(F1): This removes too much from non-contiguous subsets + mnMinSubsetActionIndex = ::std::min( + mnMinSubsetActionIndex, + rSubsetEntry.mnStartActionIndex ); + mnMaxSubsetActionIndex = ::std::max( + mnMaxSubsetActionIndex, + rSubsetEntry.mnEndActionIndex ); + } + + void DrawShapeSubsetting::updateSubsets() + { + maCurrentSubsets.clear(); + + if( !maSubsetShapes.empty() ) + { + if( maSubset.isEmpty() ) + { + // non-subsetted node, with some child subsets + // that subtract from it + maCurrentSubsets.push_back( DocTreeNode( 0, + mnMinSubsetActionIndex, + DocTreeNode::NODETYPE_INVALID ) ); + maCurrentSubsets.push_back( DocTreeNode( mnMaxSubsetActionIndex, + maActionClassVector.size(), + DocTreeNode::NODETYPE_INVALID ) ); + } + else + { + // subsetted node, from which some further child + // subsets subtract content + maCurrentSubsets.push_back( DocTreeNode( maSubset.getStartIndex(), + mnMinSubsetActionIndex, + DocTreeNode::NODETYPE_INVALID ) ); + maCurrentSubsets.push_back( DocTreeNode( mnMaxSubsetActionIndex, + maSubset.getEndIndex(), + DocTreeNode::NODETYPE_INVALID ) ); + } + } + else + { + // no further child subsets, simply add our subset (if any) + if( !maSubset.isEmpty() ) + { + // subsetted node, without any subset children + maCurrentSubsets.push_back( maSubset ); + } + } + } + + ////////////////////////////////////////////////////////////////////// + // + // Public methods + // + ////////////////////////////////////////////////////////////////////// + + DrawShapeSubsetting::DrawShapeSubsetting() : + maActionClassVector(), + mpMtf(), + maSubset(), + maSubsetShapes(), + mnMinSubsetActionIndex( SAL_MAX_INT32 ), + mnMaxSubsetActionIndex(0), + maCurrentSubsets(), + mbNodeTreeInitialized( false ) + { + } + + DrawShapeSubsetting::DrawShapeSubsetting( const GDIMetaFileSharedPtr& rMtf ) : + maActionClassVector(), + mpMtf( rMtf ), + maSubset(), + maSubsetShapes(), + mnMinSubsetActionIndex( SAL_MAX_INT32 ), + mnMaxSubsetActionIndex(0), + maCurrentSubsets(), + mbNodeTreeInitialized( false ) + { + ENSURE_OR_THROW( mpMtf, + "DrawShapeSubsetting::DrawShapeSubsetting(): Invalid metafile" ); + + initCurrentSubsets(); + } + + DrawShapeSubsetting::DrawShapeSubsetting( const DocTreeNode& rShapeSubset, + const GDIMetaFileSharedPtr& rMtf ) : + maActionClassVector(), + mpMtf( rMtf ), + maSubset( rShapeSubset ), + maSubsetShapes(), + mnMinSubsetActionIndex( SAL_MAX_INT32 ), + mnMaxSubsetActionIndex(0), + maCurrentSubsets(), + mbNodeTreeInitialized( false ) + { + ENSURE_OR_THROW( mpMtf, + "DrawShapeSubsetting::DrawShapeSubsetting(): Invalid metafile" ); + + initCurrentSubsets(); + } + + void DrawShapeSubsetting::reset() + { + maActionClassVector.clear(); + mpMtf.reset(); + maSubset.reset(); + maSubsetShapes.clear(); + mnMinSubsetActionIndex = SAL_MAX_INT32; + mnMaxSubsetActionIndex = 0; + maCurrentSubsets.clear(); + mbNodeTreeInitialized = false; + } + + void DrawShapeSubsetting::reset( const ::boost::shared_ptr< GDIMetaFile >& rMtf ) + { + reset(); + mpMtf = rMtf; + + initCurrentSubsets(); + } + + void DrawShapeSubsetting::reset( const DocTreeNode& rShapeSubset, + const ::boost::shared_ptr< GDIMetaFile >& rMtf ) + { + reset(); + mpMtf = rMtf; + maSubset = rShapeSubset; + + initCurrentSubsets(); + } + + void DrawShapeSubsetting::initCurrentSubsets() + { + // only add subset to vector, if it's not empty - that's + // because the vector's content is later literally used + // for e.g. painting. + if( !maSubset.isEmpty() ) + maCurrentSubsets.push_back( maSubset ); + } + + DocTreeNode DrawShapeSubsetting::getSubsetNode() const + { + return maSubset; + } + + bool DrawShapeSubsetting::hasSubsetShapes() const + { + return !maSubsetShapes.empty(); + } + + AttributableShapeSharedPtr DrawShapeSubsetting::getSubsetShape( const DocTreeNode& rTreeNode ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShapeSubsetting::getSubsetShape()" ); + + // subset shape already created for this DocTreeNode? + SubsetEntry aEntry; + + aEntry.mnStartActionIndex = rTreeNode.getStartIndex(); + aEntry.mnEndActionIndex = rTreeNode.getEndIndex(); + + ShapeSet::const_iterator aIter; + if( (aIter=maSubsetShapes.find( aEntry )) != maSubsetShapes.end() ) + { + // already created, return found entry + return aIter->mpShape; + } + + return AttributableShapeSharedPtr(); + } + + void DrawShapeSubsetting::addSubsetShape( const AttributableShapeSharedPtr& rShape ) + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShapeSubsetting::addSubsetShape()" ); + + // subset shape already created for this DocTreeNode? + SubsetEntry aEntry; + const DocTreeNode& rEffectiveSubset( rShape->getSubsetNode() ); + + aEntry.mnStartActionIndex = rEffectiveSubset.getStartIndex(); + aEntry.mnEndActionIndex = rEffectiveSubset.getEndIndex(); + + ShapeSet::const_iterator aIter; + if( (aIter=maSubsetShapes.find( aEntry )) != maSubsetShapes.end() ) + { + // already created, increment use count and return + + // safe cast, since set order does not depend on + // mnSubsetQueriedCount + const_cast<SubsetEntry&>(*aIter).mnSubsetQueriedCount++; + } + else + { + // not yet created, init entry + aEntry.mnSubsetQueriedCount = 1; + aEntry.mpShape = rShape; + + maSubsetShapes.insert( aEntry ); + + // update cached subset borders + updateSubsetBounds( aEntry ); + updateSubsets(); + } + } + + bool DrawShapeSubsetting::revokeSubsetShape( const AttributableShapeSharedPtr& rShape ) + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShapeSubsetting::revokeSubsetShape()" ); + + // lookup subset shape + SubsetEntry aEntry; + const DocTreeNode& rEffectiveSubset( rShape->getSubsetNode() ); + + aEntry.mnStartActionIndex = rEffectiveSubset.getStartIndex(); + aEntry.mnEndActionIndex = rEffectiveSubset.getEndIndex(); + + ShapeSet::iterator aIter; + if( (aIter=maSubsetShapes.find( aEntry )) == maSubsetShapes.end() ) + return false; // not found, subset was never queried + + // last client of the subset revoking? + if( aIter->mnSubsetQueriedCount > 1 ) + { + // no, still clients out there. Just decrement use count + // safe cast, since order does not depend on mnSubsetQueriedCount + const_cast<SubsetEntry&>(*aIter).mnSubsetQueriedCount--; + + VERBOSE_TRACE( "Subset summary: shape 0x%X, %d open subsets, revoked subset has refcount %d", + this, + maSubsetShapes.size(), + aIter->mnSubsetQueriedCount ); + + return false; // not the last client + } + + VERBOSE_TRACE( "Subset summary: shape 0x%X, %d open subsets, cleared subset has range [%d,%d]", + this, + maSubsetShapes.size(), + aEntry.mnStartActionIndex, + aEntry.mnEndActionIndex ); + + // yes, remove from set + maSubsetShapes.erase( aIter ); + + + // update currently active subset for _our_ shape (the + // part of this shape that is visible, i.e. not displayed + // in subset shapes) + // ====================================================== + + // init bounds + mnMinSubsetActionIndex = SAL_MAX_INT32; + mnMaxSubsetActionIndex = 0; + + // TODO(P2): This is quite expensive, when + // after every subset effect end, we have to scan + // the whole shape set + + // determine new subset range + ::std::for_each( maSubsetShapes.begin(), + maSubsetShapes.end(), + ::boost::bind(&DrawShapeSubsetting::updateSubsetBounds, + this, + _1 ) ); + + updateSubsets(); + + return true; + } + + namespace + { + /** Iterate over all action classification entries in the + given range, pass each element range found to the + given functor. + + This method extracts, for each of the different action + classifications, the count and the ranges for each of + them, and calls the provided functor with that + information. + + @tpl FunctorT + This is the functor's operator() calling signature, + with eCurrElemClassification denoting the current + classification type the functor is called for, + nCurrElemCount the running total of elements visited + for the given class (starting from 0), and + rCurrElemBegin/rCurrElemEnd the range of the current + element (i.e. the iterators from the start to the end + of this element). + <pre> + bool operator()( IndexClassificator eCurrElemClassification + sal_Int32 nCurrElemCount, + const IndexClassificatorVector::const_iterator& rCurrElemBegin, + const IndexClassificatorVector::const_iterator& rCurrElemEnd ); + </pre> + If the functor returns false, iteration over the + shapes is immediately stopped. + + @param io_pFunctor + This functor is called for every shape found. + + @param rBegin + Start of range to iterate over + + @param rEnd + End of range to iterate over + + @return the number of shapes found in the metafile + */ + template< typename FunctorT > void iterateActionClassifications( + FunctorT& io_rFunctor, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rBegin, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rEnd ) + { + sal_Int32 nCurrShapeCount( 0 ); + sal_Int32 nCurrParaCount( 0 ); + sal_Int32 nCurrLineCount( 0 ); + sal_Int32 nCurrSentenceCount( 0 ); + sal_Int32 nCurrWordCount( 0 ); + sal_Int32 nCurrCharCount( 0 ); + + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastShapeStart(rBegin); + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastParaStart(rBegin); + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastLineStart(rBegin); + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastSentenceStart(rBegin); + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastWordStart(rBegin); + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastCharStart(rBegin); + + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aNext; + DrawShapeSubsetting::IndexClassificatorVector::const_iterator aCurr( rBegin ); + while( aCurr != rEnd ) + { + // aNext will hold an iterator to the next element + // (or the past-the-end iterator, if aCurr + // references the last element). Used to pass a + // valid half-open range to the functors. + aNext = aCurr; + ++aNext; + + switch( *aCurr ) + { + default: + ENSURE_OR_THROW( false, + "Unexpected type in iterateDocShapes()" ); + case DrawShapeSubsetting::CLASS_NOOP: + // ignore NOOP actions + break; + + case DrawShapeSubsetting::CLASS_SHAPE_START: + // regardless of ending action + // classifications before: a new shape + // always also starts contained elements + // anew + aLastShapeStart = + aLastParaStart = + aLastLineStart = + aLastSentenceStart = + aLastWordStart = + aLastCharStart = aCurr; + break; + + case DrawShapeSubsetting::CLASS_SHAPE_END: + if( !io_rFunctor( DrawShapeSubsetting::CLASS_SHAPE_END, + nCurrShapeCount, + aLastShapeStart, + aNext ) ) + { + return; + } + + ++nCurrShapeCount; + // FALLTHROUGH intended: shape end also + // ends lines + case DrawShapeSubsetting::CLASS_PARAGRAPH_END: + if( !io_rFunctor( DrawShapeSubsetting::CLASS_PARAGRAPH_END, + nCurrParaCount, + aLastParaStart, + aNext ) ) + { + return; + } + + ++nCurrParaCount; + aLastParaStart = aNext; + // FALLTHROUGH intended: para end also + // ends line + case DrawShapeSubsetting::CLASS_LINE_END: + if( !io_rFunctor( DrawShapeSubsetting::CLASS_LINE_END, + nCurrLineCount, + aLastLineStart, + aNext ) ) + { + return; + } + + ++nCurrLineCount; + aLastLineStart = aNext; + + if( *aCurr == DrawShapeSubsetting::CLASS_LINE_END ) + { + // DON'T fall through here, as a line + // does NOT end neither a sentence, + // nor a word. OTOH, all parent + // structures (paragraph and shape), + // which itself fall through to this + // code, DO end word, sentence and + // character cell. + + // TODO(F1): Maybe a line should end a + // character cell, OTOH? + break; + } + // FALLTHROUGH intended + case DrawShapeSubsetting::CLASS_SENTENCE_END: + if( !io_rFunctor( DrawShapeSubsetting::CLASS_SENTENCE_END, + nCurrSentenceCount, + aLastSentenceStart, + aNext ) ) + { + return; + } + + ++nCurrSentenceCount; + aLastSentenceStart = aNext; + // FALLTHROUGH intended + case DrawShapeSubsetting::CLASS_WORD_END: + if( !io_rFunctor( DrawShapeSubsetting::CLASS_WORD_END, + nCurrWordCount, + aLastWordStart, + aNext ) ) + { + return; + } + + ++nCurrWordCount; + aLastWordStart = aNext; + // FALLTHROUGH intended + case DrawShapeSubsetting::CLASS_CHARACTER_CELL_END: + if( !io_rFunctor( DrawShapeSubsetting::CLASS_CHARACTER_CELL_END, + nCurrCharCount, + aLastCharStart, + aNext ) ) + { + return; + } + + ++nCurrCharCount; + aLastCharStart = aNext; + break; + } + + aCurr = aNext; + } + } + + DrawShapeSubsetting::IndexClassificator mapDocTreeNode( DocTreeNode::NodeType eNodeType ) + { + switch( eNodeType ) + { + case DocTreeNode::NODETYPE_INVALID: + // FALLTHROUGH intended + default: + OSL_ENSURE(false, + "DrawShapeSubsetting::mapDocTreeNode(): unexpected node type"); + return DrawShapeSubsetting::CLASS_NOOP; + + case DocTreeNode::NODETYPE_LOGICAL_SHAPE: + // FALLTHROUGH intended + case DocTreeNode::NODETYPE_FORMATTING_SHAPE: + return DrawShapeSubsetting::CLASS_SHAPE_END; + + case DocTreeNode::NODETYPE_FORMATTING_LINE: + return DrawShapeSubsetting::CLASS_LINE_END; + + case DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH: + return DrawShapeSubsetting::CLASS_PARAGRAPH_END; + + case DocTreeNode::NODETYPE_LOGICAL_SENTENCE: + return DrawShapeSubsetting::CLASS_SENTENCE_END; + + case DocTreeNode::NODETYPE_LOGICAL_WORD: + return DrawShapeSubsetting::CLASS_WORD_END; + + case DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL: + return DrawShapeSubsetting::CLASS_CHARACTER_CELL_END; + }; + } + + /// Counts number of class occurences + class CountClassFunctor + { + public: + CountClassFunctor( DrawShapeSubsetting::IndexClassificator eClass ) : + meClass( eClass ), + mnCurrCount(0) + { + } + + bool operator()( DrawShapeSubsetting::IndexClassificator eCurrElemClassification, + sal_Int32 /*nCurrElemCount*/, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& /*rCurrElemBegin*/, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& /*rCurrElemEnd*/ ) + { + if( eCurrElemClassification == meClass ) + ++mnCurrCount; + + return true; // never stop, count all occurences + } + + sal_Int32 getCount() const + { + return mnCurrCount; + } + + private: + DrawShapeSubsetting::IndexClassificator meClass; + sal_Int32 mnCurrCount; + }; + } + + sal_Int32 DrawShapeSubsetting::implGetNumberOfTreeNodes( const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rBegin, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rEnd, + DocTreeNode::NodeType eNodeType ) const + { + const IndexClassificator eRequestedClass( + mapDocTreeNode( eNodeType ) ); + + // create a counting functor for the requested class of + // actions + CountClassFunctor aFunctor( eRequestedClass ); + + // count all occurences in the given range + iterateActionClassifications( aFunctor, rBegin, rEnd ); + + return aFunctor.getCount(); + } + + sal_Int32 DrawShapeSubsetting::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const + { + ensureInitializedNodeTree(); + + return implGetNumberOfTreeNodes( maActionClassVector.begin(), + maActionClassVector.end(), + eNodeType ); + } + + namespace + { + /** This functor finds the nth occurrence of a given + action class. + + The operator() compares the given index value with the + requested index, as given on the functor's + constructor. Then, the operator() returns false, + denoting that the requested action is found. + */ + class FindNthElementFunctor + { + public: + FindNthElementFunctor( sal_Int32 nNodeIndex, + DrawShapeSubsetting::IndexClassificator eClass ) : + mnNodeIndex( nNodeIndex ), + meClass( eClass ) + { + } + + bool operator()( DrawShapeSubsetting::IndexClassificator eCurrElemClassification, + sal_Int32 nCurrElemCount, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rCurrElemBegin, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rCurrElemEnd ) + { + if( eCurrElemClassification == meClass && + nCurrElemCount == mnNodeIndex ) + { + maLastBegin = rCurrElemBegin; + maLastEnd = rCurrElemEnd; + + return false; // abort iteration, we've + // already found what we've been + // looking for + } + + return true; // keep on truckin' + } + + DrawShapeSubsetting::IndexClassificatorVector::const_iterator getBeginElement() const + { + return maLastBegin; + } + + DrawShapeSubsetting::IndexClassificatorVector::const_iterator getEndElement() const + { + return maLastEnd; + } + + private: + sal_Int32 mnNodeIndex; + DrawShapeSubsetting::IndexClassificatorVector::const_iterator maLastBegin; + DrawShapeSubsetting::IndexClassificatorVector::const_iterator maLastEnd; + DrawShapeSubsetting::IndexClassificator meClass; + }; + + DocTreeNode makeTreeNode( const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rBegin, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rStart, + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rEnd, + DocTreeNode::NodeType eNodeType ) + { + return DocTreeNode( ::std::distance(rBegin, + rStart), + ::std::distance(rBegin, + rEnd), + eNodeType ); + } + } + + DocTreeNode DrawShapeSubsetting::implGetTreeNode( const IndexClassificatorVector::const_iterator& rBegin, + const IndexClassificatorVector::const_iterator& rEnd, + sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const + { + const IndexClassificator eRequestedClass( + mapDocTreeNode( eNodeType ) ); + + // create a nth element functor for the requested class of + // actions, and nNodeIndex as the target index + FindNthElementFunctor aFunctor( nNodeIndex, + eRequestedClass ); + + // find given index in the given range + iterateActionClassifications( aFunctor, rBegin, rEnd ); + + return makeTreeNode( maActionClassVector.begin(), + aFunctor.getBeginElement(), + aFunctor.getEndElement(), + eNodeType ); + } + + DocTreeNode DrawShapeSubsetting::getTreeNode( sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const + { + ensureInitializedNodeTree(); + + return implGetTreeNode( maActionClassVector.begin(), + maActionClassVector.end(), + nNodeIndex, + eNodeType ); + } + + sal_Int32 DrawShapeSubsetting::getNumberOfSubsetTreeNodes( const DocTreeNode& rParentNode, + DocTreeNode::NodeType eNodeType ) const + { + ensureInitializedNodeTree(); + + // convert from vector indices to vector iterators + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aBegin( maActionClassVector.begin() ); + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentBegin( aBegin + rParentNode.getStartIndex() ); + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentEnd( aBegin + rParentNode.getEndIndex() ); + + return implGetNumberOfTreeNodes( aParentBegin, + aParentEnd, + eNodeType ); + } + + DocTreeNode DrawShapeSubsetting::getSubsetTreeNode( const DocTreeNode& rParentNode, + sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const + { + ensureInitializedNodeTree(); + + // convert from vector indices to vector iterators + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aBegin( maActionClassVector.begin() ); + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentBegin( aBegin + rParentNode.getStartIndex() ); + const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentEnd( aBegin + rParentNode.getEndIndex() ); + + return implGetTreeNode( aParentBegin, + aParentEnd, + nNodeIndex, + eNodeType ); + } + + const VectorOfDocTreeNodes& DrawShapeSubsetting::getActiveSubsets() const + { + return maCurrentSubsets; + } + + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/drawshapesubsetting.hxx b/slideshow/source/engine/shapes/drawshapesubsetting.hxx new file mode 100644 index 000000000000..1dc560a9b97c --- /dev/null +++ b/slideshow/source/engine/shapes/drawshapesubsetting.hxx @@ -0,0 +1,291 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_DRAWSHAPESUBSETTING_HXX +#define INCLUDED_SLIDESHOW_DRAWSHAPESUBSETTING_HXX + +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + +#include "doctreenode.hxx" +#include "attributableshape.hxx" + + +class GDIMetaFile; + +namespace slideshow +{ + namespace internal + { + /** This class encapsulates the subsetting aspects of a + DrawShape. + */ + class DrawShapeSubsetting : private boost::noncopyable + { + public: + /** Create empty shape subset handling. + + This method creates a subset handler which contains no + subset information. All methods will return default + values. + + @param rMtf + Metafile to retrieve subset info from (must have been + generated with verbose text comments switched on). + */ + DrawShapeSubsetting(); + + /** Create new shape subset handling. + + This method creates a subset handler which initially + displays the whole shape. + + @param rMtf + Metafile to retrieve subset info from (must have been + generated with verbose text comments switched on). + */ + explicit DrawShapeSubsetting( const ::boost::shared_ptr< GDIMetaFile >& rMtf ); + + /** Create new shape subset handling. + + @param rShapeSubset + The subset this object represents (can be empty, then + denoting 'represents a whole shape') + + @param rMtf + Metafile to retrieve subset info from (must have been + generated with verbose text comments switched on). + */ + DrawShapeSubsetting( const DocTreeNode& rShapeSubset, + const ::boost::shared_ptr< GDIMetaFile >& rMtf ); + + /** Reset metafile. + + Use this method to completely reset the + ShapeSubsetting, with a new metafile. Note that any + information previously set will be lost, including + added subset shapes! + + @param rMtf + Metafile to retrieve subset info from (must have been + generated with verbose text comments switched on). + */ + void reset( const ::boost::shared_ptr< GDIMetaFile >& rMtf ); + + /** Reset metafile and subset. + + Use this method to completely reset the + ShapeSubsetting, with a new metafile and subset + range. Note that any information previously set will + be lost, including added subset shapes! + + @param rShapeSubset + The subset this object represents (can be empty, then + denoting 'represents a whole shape') + + @param rMtf + Metafile to retrieve subset info from (must have been + generated with verbose text comments switched on). + */ + void reset( const DocTreeNode& rShapeSubset, + const ::boost::shared_ptr< GDIMetaFile >& rMtf ); + + + // Shape subsetting methods + // ======================================================== + + /// Return subset node for this shape + DocTreeNode getSubsetNode () const; + + /// Return true, if any child subset shapes exist + bool hasSubsetShapes () const; + + /// Get subset shape for given node, if any + AttributableShapeSharedPtr getSubsetShape ( const DocTreeNode& rTreeNode ) const; + + /// Add child subset shape (or increase use count, if already existent) + void addSubsetShape ( const AttributableShapeSharedPtr& rShape ); + + /** Revoke subset shape + + This method revokes a subset shape, decrementing the + use count for this subset by one. If the use count + reaches zero (i.e. when the number of addSubsetShape() + matches the number of revokeSubsetShape() calls for + the same subset), the subset entry is removed from the + internal list, and subsequent getSubsetShape() calls + will return the empty pointer for this subset. + + @return true, if the subset shape was physically + removed from the list (false is returned, when nothing + was removed, either because only the use count was + decremented, or there was no such subset found, in the + first place). + */ + bool revokeSubsetShape ( const AttributableShapeSharedPtr& rShape ); + + + // Doc tree methods + // ======================================================== + + /// Return overall number of nodes for given type + sal_Int32 getNumberOfTreeNodes ( DocTreeNode::NodeType eNodeType ) const; + + /// Return tree node of given index and given type + DocTreeNode getTreeNode ( sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const; + + /// Return number of nodes of given type, below parent node + sal_Int32 getNumberOfSubsetTreeNodes ( const DocTreeNode& rParentNode, + DocTreeNode::NodeType eNodeType ) const; + + /// Return tree node of given index and given type, relative to parent node + DocTreeNode getSubsetTreeNode ( const DocTreeNode& rParentNode, + sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const; + + // Helper + // ======================================================== + + /** Return a vector of currently active subsets. + + Needed when rendering a shape, this method provides a + vector of subsets currently visible (the range as + returned by getEffectiveSubset(), minus the parts that + are currently hidden, because displayed by child + shapes). + */ + const VectorOfDocTreeNodes& getActiveSubsets() const; + + /** This enum classifies each action index in the + metafile. + + Of interest are, of course, the places where + structural shape and/or text elements end. The + remainder of the action gets classified as 'noop' + */ + enum IndexClassificator + { + CLASS_NOOP, + CLASS_SHAPE_START, + CLASS_SHAPE_END, + + CLASS_LINE_END, + CLASS_PARAGRAPH_END, + CLASS_SENTENCE_END, + CLASS_WORD_END, + CLASS_CHARACTER_CELL_END + }; + + typedef ::std::vector< IndexClassificator > IndexClassificatorVector; + + private: + /** Entry for subset shape + + This struct contains data for every subset shape + generated. Note that for a given start/end action + index combination, only one subset instance is + generated (and reused for subsequent queries). + */ + struct SubsetEntry + { + AttributableShapeSharedPtr mpShape; + sal_Int32 mnStartActionIndex; + sal_Int32 mnEndActionIndex; + + /// Number of times this subset was queried, and not yet revoked + int mnSubsetQueriedCount; + + sal_Int32 getHashValue() const + { + // TODO(Q3): That's a hack. We assume that start + // index will always be less than 65535 (if this + // assumption is violated, hash map performance + // will degrade severely) + return mnStartActionIndex*SAL_MAX_INT16 + mnEndActionIndex; + } + + /// The shape set is ordered according to this method + bool operator<(const SubsetEntry& rOther) const + { + return getHashValue() < rOther.getHashValue(); + } + + }; + + typedef ::std::set< SubsetEntry > ShapeSet; + + void ensureInitializedNodeTree() const; + void updateSubsetBounds( const SubsetEntry& rSubsetEntry ); + void updateSubsets(); + void initCurrentSubsets(); + void reset(); + + sal_Int32 implGetNumberOfTreeNodes( const IndexClassificatorVector::const_iterator& rBegin, + const IndexClassificatorVector::const_iterator& rEnd, + DocTreeNode::NodeType eNodeType ) const; + DocTreeNode implGetTreeNode( const IndexClassificatorVector::const_iterator& rBegin, + const IndexClassificatorVector::const_iterator& rEnd, + sal_Int32 nNodeIndex, + DocTreeNode::NodeType eNodeType ) const; + + mutable IndexClassificatorVector maActionClassVector; + + /// Metafile to retrieve subset info from + ::boost::shared_ptr< GDIMetaFile > mpMtf; + + /// Subset of the metafile represented by this object + DocTreeNode maSubset; + + /// the list of subset shapes spawned from this one. + ShapeSet maSubsetShapes; + + /// caches minimal subset index from maSubsetShapes + sal_Int32 mnMinSubsetActionIndex; + + /// caches maximal subset index from maSubsetShapes + sal_Int32 mnMaxSubsetActionIndex; + + /** Current number of subsets to render (calculated from + maSubset and mnMin/MaxSubsetActionIndex). + + Note that this is generally _not_ equivalent to + maSubset, as it excludes all active subset children! + */ + mutable VectorOfDocTreeNodes maCurrentSubsets; + + /// Whether the shape's doc tree has been initialized successfully, or not + mutable bool mbNodeTreeInitialized; + }; + + } +} + +#endif /* INCLUDED_SLIDESHOW_DRAWSHAPESUBSETTING_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/externalshapebase.cxx b/slideshow/source/engine/shapes/externalshapebase.cxx new file mode 100644 index 000000000000..7874143ca916 --- /dev/null +++ b/slideshow/source/engine/shapes/externalshapebase.cxx @@ -0,0 +1,249 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <comphelper/anytostring.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include "externalshapebase.hxx" +#include "eventmultiplexer.hxx" +#include "vieweventhandler.hxx" +#include "intrinsicanimationeventhandler.hxx" +#include "tools.hxx" + +#include <boost/noncopyable.hpp> + + +using namespace ::com::sun::star; + + +namespace slideshow +{ + namespace internal + { + class ExternalShapeBase::ExternalShapeBaseListener : public ViewEventHandler, + public IntrinsicAnimationEventHandler, + private boost::noncopyable + { + public: + explicit ExternalShapeBaseListener( ExternalShapeBase& rBase ) : + mrBase( rBase ) + {} + + + private: + // ViewEventHandler + // ------------------------------------------------- + + virtual void viewAdded( const UnoViewSharedPtr& ) {} + virtual void viewRemoved( const UnoViewSharedPtr& ) {} + virtual void viewChanged( const UnoViewSharedPtr& rView ) + { + mrBase.implViewChanged(rView); + } + virtual void viewsChanged() + { + mrBase.implViewsChanged(); + } + + + // IntrinsicAnimationEventHandler + // ------------------------------------------------- + + virtual bool enableAnimations() + { + return mrBase.implStartIntrinsicAnimation(); + } + virtual bool disableAnimations() + { + return mrBase.implEndIntrinsicAnimation(); + } + + ExternalShapeBase& mrBase; + }; + + + ExternalShapeBase::ExternalShapeBase( const uno::Reference< drawing::XShape >& xShape, + double nPrio, + const SlideShowContext& rContext ) : + mxComponentContext( rContext.mxComponentContext ), + mxShape( xShape ), + mpListener( new ExternalShapeBaseListener(*this) ), + mpShapeManager( rContext.mpSubsettableShapeManager ), + mrEventMultiplexer( rContext.mrEventMultiplexer ), + mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ), + maBounds( getAPIShapeBounds( xShape ) ) + { + ENSURE_OR_THROW( mxShape.is(), "ExternalShapeBase::ExternalShapeBase(): Invalid XShape" ); + + mpShapeManager->addIntrinsicAnimationHandler( mpListener ); + mrEventMultiplexer.addViewHandler( mpListener ); + } + + // --------------------------------------------------------------------- + + ExternalShapeBase::~ExternalShapeBase() + { + try + { + mrEventMultiplexer.removeViewHandler( mpListener ); + mpShapeManager->removeIntrinsicAnimationHandler( mpListener ); + } + catch (uno::Exception &) + { + OSL_ENSURE( false, rtl::OUStringToOString( + comphelper::anyToString( + cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + + // --------------------------------------------------------------------- + + uno::Reference< drawing::XShape > ExternalShapeBase::getXShape() const + { + return mxShape; + } + + // --------------------------------------------------------------------- + + void ExternalShapeBase::play() + { + implStartIntrinsicAnimation(); + } + + // --------------------------------------------------------------------- + + void ExternalShapeBase::stop() + { + implEndIntrinsicAnimation(); + } + + // --------------------------------------------------------------------- + + void ExternalShapeBase::pause() + { + implPauseIntrinsicAnimation(); + } + + // --------------------------------------------------------------------- + + bool ExternalShapeBase::isPlaying() const + { + return implIsIntrinsicAnimationPlaying(); + } + + // --------------------------------------------------------------------- + + void ExternalShapeBase::setMediaTime(double fTime) + { + implSetIntrinsicAnimationTime(fTime); + } + + // --------------------------------------------------------------------- + + bool ExternalShapeBase::update() const + { + return render(); + } + + // --------------------------------------------------------------------- + + bool ExternalShapeBase::render() const + { + if( maBounds.getRange().equalZero() ) + { + // zero-sized shapes are effectively invisible, + // thus, we save us the rendering... + return true; + } + + return implRender( maBounds ); + } + + // --------------------------------------------------------------------- + + bool ExternalShapeBase::isContentChanged() const + { + return true; + } + + // --------------------------------------------------------------------- + + ::basegfx::B2DRectangle ExternalShapeBase::getBounds() const + { + return maBounds; + } + + // --------------------------------------------------------------------- + + ::basegfx::B2DRectangle ExternalShapeBase::getDomBounds() const + { + return maBounds; + } + + // --------------------------------------------------------------------- + + ::basegfx::B2DRectangle ExternalShapeBase::getUpdateArea() const + { + return maBounds; + } + + // --------------------------------------------------------------------- + + bool ExternalShapeBase::isVisible() const + { + return true; + } + + // --------------------------------------------------------------------- + + double ExternalShapeBase::getPriority() const + { + return mnPriority; + } + + // --------------------------------------------------------------------- + + bool ExternalShapeBase::isBackgroundDetached() const + { + // external shapes always have their own window/surface + return true; + } + + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/externalshapebase.hxx b/slideshow/source/engine/shapes/externalshapebase.hxx new file mode 100644 index 000000000000..ecde71bc7b54 --- /dev/null +++ b/slideshow/source/engine/shapes/externalshapebase.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_EXTERNALSHAPEBASE_HXX +#define INCLUDED_SLIDESHOW_EXTERNALSHAPEBASE_HXX + +#include <vector> + +#include "externalmediashape.hxx" +#include "unoview.hxx" +#include "subsettableshapemanager.hxx" +#include "slideshowexceptions.hxx" +#include "slideshowcontext.hxx" + + +namespace slideshow +{ + namespace internal + { + /** Base class for shapes rendered by external engines. + + Used as the common base for e.g. MediaShape or + AppletShape, all of which are rendered by external + components (and all employ distinct windows). + + Please note that this base class indeed assumes the shape + does not interfere with the internal shapes in any way + (including mutual overdraw). It therefore reports yes for + the isBackgroundDetached() question. + */ + class ExternalShapeBase : public ExternalMediaShape + { + public: + /** Create a shape for the given XShape for an external shape + + @param xShape + The XShape to represent. + + @param nPrio + Externally-determined shape priority (used e.g. for + paint ordering). This number _must be_ unique! + */ + ExternalShapeBase( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + double nPrio, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + virtual ~ExternalShapeBase(); + + virtual ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape > getXShape() const; + + // animation methods + //------------------------------------------------------------------ + + virtual void play(); + virtual void stop(); + virtual void pause(); + virtual bool isPlaying() const; + virtual void setMediaTime(double); + + // render methods + //------------------------------------------------------------------ + + virtual bool update() const; + virtual bool render() const; + virtual bool isContentChanged() const; + + + // Shape attributes + //------------------------------------------------------------------ + + virtual ::basegfx::B2DRectangle getBounds() const; + virtual ::basegfx::B2DRectangle getDomBounds() const; + virtual ::basegfx::B2DRectangle getUpdateArea() const; + virtual bool isVisible() const; + virtual double getPriority() const; + virtual bool isBackgroundDetached() const; + + protected: + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XComponentContext> mxComponentContext; + + private: + class ExternalShapeBaseListener; friend class ExternalShapeBaseListener; + + /// override in derived class to render preview + virtual bool implRender( const ::basegfx::B2DRange& rCurrBounds ) const = 0; + + /// override in derived class to resize + virtual void implViewChanged( const UnoViewSharedPtr& rView ) = 0; + /// override in derived class to resize + virtual void implViewsChanged() = 0; + + /// override in derived class to start external viewer + virtual bool implStartIntrinsicAnimation() = 0; + /// override in derived class to stop external viewer + virtual bool implEndIntrinsicAnimation() = 0; + /// override in derived class to pause external viewer + virtual bool implPauseIntrinsicAnimation() = 0; + /// override in derived class to return status of animation + virtual bool implIsIntrinsicAnimationPlaying() const = 0; + /// override in derived class to set media time + virtual void implSetIntrinsicAnimationTime(double) = 0; + + + /// The associated XShape + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape > mxShape; + + boost::shared_ptr<ExternalShapeBaseListener> mpListener; + + SubsettableShapeManagerSharedPtr mpShapeManager; + EventMultiplexer& mrEventMultiplexer; + + // The attributes of this Shape + const double mnPriority; + ::basegfx::B2DRectangle maBounds; + }; + } +} + +#endif /* INCLUDED_SLIDESHOW_EXTERNALSHAPEBASE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/gdimtftools.cxx b/slideshow/source/engine/shapes/gdimtftools.cxx new file mode 100644 index 000000000000..9f3252453e30 --- /dev/null +++ b/slideshow/source/engine/shapes/gdimtftools.cxx @@ -0,0 +1,551 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <gdimtftools.hxx> + +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/graphic/XGraphicRenderer.hpp> +#include <com/sun/star/drawing/XShape.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase1.hxx> + +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase1.hxx> + +#include <tools/stream.hxx> +#include <vcl/svapp.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/metaact.hxx> +#include <vcl/virdev.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> +#include <vcl/animate.hxx> +#include <vcl/graph.hxx> + +#include <unotools/streamwrap.hxx> + +#include "tools.hxx" + +using namespace ::com::sun::star; + + +// free support functions +// ====================== + +namespace slideshow +{ +namespace internal +{ +// TODO(E2): Detect the case when svx/drawing layer is not +// in-process, or even not on the same machine, and +// fallback to metafile streaming! + +// For fixing #i48102#, have to be a _lot_ more selective +// on which metafiles to convert to bitmaps. The problem +// here is that we _always_ get the shape content as a +// metafile, even if we have a bitmap graphic shape. Thus, +// calling GetBitmapEx on such a Graphic (see below) will +// result in one poorly scaled bitmap into another, +// somewhat arbitrarily sized bitmap. +bool hasUnsupportedActions( const GDIMetaFile& rMtf ) +{ + // search metafile for RasterOp action + MetaAction* pCurrAct; + + // TODO(Q3): avoid const-cast + for( pCurrAct = const_cast<GDIMetaFile&>(rMtf).FirstAction(); + pCurrAct; + pCurrAct = const_cast<GDIMetaFile&>(rMtf).NextAction() ) + { + switch( pCurrAct->GetType() ) + { + case META_RASTEROP_ACTION: + // overpaint is okay - that's the default, anyway + if( ROP_OVERPAINT == + static_cast<MetaRasterOpAction*>(pCurrAct)->GetRasterOp() ) + { + break; + } + // FALLTHROUGH intended + case META_MOVECLIPREGION_ACTION: + // FALLTHROUGH intended + case META_REFPOINT_ACTION: + // FALLTHROUGH intended + case META_WALLPAPER_ACTION: + return true; // at least one unsupported + // action encountered + } + } + + return false; // no unsupported action found +} + +namespace { + +typedef ::cppu::WeakComponentImplHelper1< graphic::XGraphicRenderer > DummyRenderer_Base; + +class DummyRenderer : + public DummyRenderer_Base, + public cppu::BaseMutex +{ +public: + DummyRenderer() : + DummyRenderer_Base( m_aMutex ), + mxGraphic() + { + } + + //--- XGraphicRenderer ----------------------------------- + virtual void SAL_CALL render( const uno::Reference< graphic::XGraphic >& rGraphic ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + mxGraphic = rGraphic; + } + + /** Retrieve GDIMetaFile from renderer + + @param bForeignSource + When true, the source of the metafile might be a + foreign application. The metafile is checked + against unsupported content, and, if necessary, + returned as a pre-rendererd bitmap. + */ + GDIMetaFile getMtf( bool bForeignSource ) const + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Graphic aGraphic( mxGraphic ); + + if( aGraphic.GetType() == GRAPHIC_BITMAP || + (bForeignSource && + hasUnsupportedActions(aGraphic.GetGDIMetaFile()) ) ) + { + // wrap bitmap into GDIMetafile + GDIMetaFile aMtf; + ::Point aEmptyPoint; + + ::BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); + + aMtf.AddAction( new MetaBmpExAction( aEmptyPoint, + aBmpEx ) ); + aMtf.SetPrefSize( aBmpEx.GetPrefSize() ); + aMtf.SetPrefMapMode( aBmpEx.GetPrefMapMode() ); + + return aMtf; + } + else + { + return aGraphic.GetGDIMetaFile(); + } + } + +private: + uno::Reference< graphic::XGraphic > mxGraphic; +}; + +} // anon namespace + +// Quick'n'dirty way: tunnel Graphic (only works for +// in-process slideshow, of course) +bool getMetaFile( const uno::Reference< lang::XComponent >& xSource, + const uno::Reference< drawing::XDrawPage >& xContainingPage, + GDIMetaFile& rMtf, + int mtfLoadFlags, + const uno::Reference< uno::XComponentContext >& rxContext ) +{ + ENSURE_OR_RETURN_FALSE( rxContext.is(), + "getMetaFile(): Invalid context" ); + + // create dummy XGraphicRenderer, which receives the + // generated XGraphic from the GraphicExporter + + // TODO(P3): Move creation of DummyRenderer out of the + // loop! Either by making it static, or transforming + // the whole thing here into a class. + DummyRenderer* pRenderer( new DummyRenderer() ); + uno::Reference< graphic::XGraphicRenderer > xRenderer( pRenderer ); + + // -> stuff that into UnoGraphicExporter. + uno::Reference<lang::XMultiComponentFactory> xFactory( + rxContext->getServiceManager() ); + + OSL_ENSURE( xFactory.is(), "### no UNO?!" ); + if( !xFactory.is() ) + return false; + + // creating the graphic exporter + uno::Reference< document::XExporter > xExporter( + xFactory->createInstanceWithContext( + OUSTR("com.sun.star.drawing.GraphicExportFilter"), + rxContext), + uno::UNO_QUERY ); + uno::Reference< document::XFilter > xFilter( xExporter, uno::UNO_QUERY ); + + OSL_ENSURE( xExporter.is() && xFilter.is(), "### no graphic exporter?!" ); + if( !xExporter.is() || !xFilter.is() ) + return false; + + uno::Sequence< beans::PropertyValue > aProps(3); + aProps[0].Name = OUSTR("FilterName"); + aProps[0].Value <<= OUSTR("SVM"); + + aProps[1].Name = OUSTR("GraphicRenderer"); + aProps[1].Value <<= xRenderer; + + uno::Sequence< beans::PropertyValue > aFilterData(5); + aFilterData[0].Name = OUSTR("VerboseComments"); + aFilterData[0].Value <<= ((mtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) != 0); + + aFilterData[1].Name = OUSTR("ScrollText"); + aFilterData[1].Value <<= ((mtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != 0); + + aFilterData[2].Name = OUSTR("ExportOnlyBackground"); + aFilterData[2].Value <<= ((mtfLoadFlags & MTF_LOAD_BACKGROUND_ONLY) != 0); + + aFilterData[3].Name = OUSTR("Version"); + aFilterData[3].Value <<= static_cast<sal_Int32>( SOFFICE_FILEFORMAT_50 ); + + aFilterData[4].Name = OUSTR("CurrentPage"); + aFilterData[4].Value <<= uno::Reference< uno::XInterface >( xContainingPage, + uno::UNO_QUERY_THROW ); + + aProps[2].Name = OUSTR("FilterData"); + aProps[2].Value <<= aFilterData; + + xExporter->setSourceDocument( xSource ); + if( !xFilter->filter( aProps ) ) + return false; + + rMtf = pRenderer->getMtf( (mtfLoadFlags & MTF_LOAD_FOREIGN_SOURCE) != 0 ); + + // pRenderer is automatically destroyed when xRenderer + // goes out of scope + + // TODO(E3): Error handling. Exporter might have + // generated nothing, a bitmap, threw an exception, + // whatever. + return true; +} + +void removeTextActions( GDIMetaFile& rMtf ) +{ + // search metafile for text output + MetaAction* pCurrAct; + + int nActionIndex(0); + pCurrAct = rMtf.FirstAction(); + while( pCurrAct ) + { + switch( pCurrAct->GetType() ) + { + case META_TEXTCOLOR_ACTION: + case META_TEXTFILLCOLOR_ACTION: + case META_TEXTLINECOLOR_ACTION: + case META_TEXTALIGN_ACTION: + case META_FONT_ACTION: + case META_LAYOUTMODE_ACTION: + case META_TEXT_ACTION: + case META_TEXTARRAY_ACTION: + case META_TEXTRECT_ACTION: + case META_STRETCHTEXT_ACTION: + case META_TEXTLINE_ACTION: + { + // remove every text-related actions + pCurrAct = rMtf.NextAction(); + + rMtf.RemoveAction( nActionIndex ); + break; + } + + default: + pCurrAct = rMtf.NextAction(); + ++nActionIndex; + break; + } + } +} + +sal_Int32 getNextActionOffset( MetaAction * pCurrAct ) +{ + // Special handling for actions that represent + // more than one indexable action + // =========================================== + + switch (pCurrAct->GetType()) { + case META_TEXT_ACTION: { + MetaTextAction * pAct = static_cast<MetaTextAction *>(pCurrAct); + return (pAct->GetLen() == (USHORT)STRING_LEN + ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen()); + } + case META_TEXTARRAY_ACTION: { + MetaTextArrayAction * pAct = + static_cast<MetaTextArrayAction *>(pCurrAct); + return (pAct->GetLen() == (USHORT)STRING_LEN + ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen()); + } + case META_STRETCHTEXT_ACTION: { + MetaStretchTextAction * pAct = + static_cast<MetaStretchTextAction *>(pCurrAct); + return (pAct->GetLen() == (USHORT)STRING_LEN + ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen()); + } + case META_FLOATTRANSPARENT_ACTION: { + MetaFloatTransparentAction * pAct = + static_cast<MetaFloatTransparentAction*>(pCurrAct); + // TODO(F2): Recurse into action metafile + // (though this is currently not used from the + // DrawingLayer - shape transparency gradients + // don't affect shape text) + return pAct->GetGDIMetaFile().GetActionCount(); + } + default: + return 1; + } +} + +bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames, + ::std::size_t& o_rLoopCount, + CycleMode& o_eCycleMode, + const Graphic& rGraphic ) +{ + o_rFrames.clear(); + + if( !rGraphic.IsAnimated() ) + return false; + + // some loop invariants + Animation aAnimation( rGraphic.GetAnimation() ); + const Point aEmptyPoint; + const Size aAnimSize( aAnimation.GetDisplaySizePixel() ); + + // setup VDev, into which all bitmaps are painted (want to + // normalize animations to n bitmaps of same size. An Animation, + // though, can contain bitmaps of varying sizes and different + // update modes) + VirtualDevice aVDev; + aVDev.SetOutputSizePixel( aAnimSize ); + aVDev.EnableMapMode( FALSE ); + + // setup mask VDev (alpha VDev is currently rather slow) + VirtualDevice aVDevMask; + aVDevMask.SetOutputSizePixel( aAnimSize ); + aVDevMask.EnableMapMode( FALSE ); + + switch( aAnimation.GetCycleMode() ) + { + case CYCLE_NOT: + o_rLoopCount = 1; + o_eCycleMode = CYCLE_LOOP; + break; + + case CYCLE_FALLBACK: + // FALLTHROUGH intended + case CYCLE_NORMAL: + o_rLoopCount = aAnimation.GetLoopCount(); + o_eCycleMode = CYCLE_LOOP; + break; + + case CYCLE_REVERS: + // FALLTHROUGH intended + case CYCLE_REVERS_FALLBACK: + o_rLoopCount = aAnimation.GetLoopCount(); + o_eCycleMode = CYCLE_PINGPONGLOOP; + break; + + default: + ENSURE_OR_RETURN_FALSE(false, + "getAnimationFromGraphic(): Unexpected case" ); + break; + } + + for( USHORT i=0, nCount=aAnimation.Count(); i<nCount; ++i ) + { + const AnimationBitmap& rAnimBmp( aAnimation.Get(i) ); + switch(rAnimBmp.eDisposal) + { + case DISPOSE_NOT: + { + aVDev.DrawBitmapEx(rAnimBmp.aPosPix, + rAnimBmp.aBmpEx); + Bitmap aMask = rAnimBmp.aBmpEx.GetMask(); + + if( aMask.IsEmpty() ) + { + const Point aEmpty; + const Rectangle aRect(aEmptyPoint, + aVDevMask.GetOutputSizePixel()); + const Wallpaper aWallpaper(COL_BLACK); + aVDevMask.DrawWallpaper(aRect, + aWallpaper); + } + else + { + BitmapEx aTmpMask = BitmapEx(aMask, + aMask); + aVDevMask.DrawBitmapEx(rAnimBmp.aPosPix, + aTmpMask ); + } + break; + } + + case DISPOSE_BACK: + { + // #i70772# react on no mask + const Bitmap aMask(rAnimBmp.aBmpEx.GetMask()); + const Bitmap aContent(rAnimBmp.aBmpEx.GetBitmap()); + + aVDevMask.Erase(); + aVDev.DrawBitmap(rAnimBmp.aPosPix, aContent); + + if(aMask.IsEmpty()) + { + const Rectangle aRect(rAnimBmp.aPosPix, aContent.GetSizePixel()); + aVDevMask.SetFillColor(COL_BLACK); + aVDevMask.SetLineColor(); + aVDevMask.DrawRect(aRect); + } + else + { + aVDevMask.DrawBitmap(rAnimBmp.aPosPix, aMask); + } + break; + } + + case DISPOSE_FULL: + { + aVDev.DrawBitmapEx(rAnimBmp.aPosPix, + rAnimBmp.aBmpEx); + break; + } + + case DISPOSE_PREVIOUS : + { + aVDev.DrawBitmapEx(rAnimBmp.aPosPix, + rAnimBmp.aBmpEx); + aVDevMask.DrawBitmap(rAnimBmp.aPosPix, + rAnimBmp.aBmpEx.GetMask()); + break; + } + } + + // extract current aVDev content into a new animation + // frame + GDIMetaFileSharedPtr pMtf( new GDIMetaFile() ); + pMtf->AddAction( + new MetaBmpExAction( aEmptyPoint, + BitmapEx( + aVDev.GetBitmap( + aEmptyPoint, + aAnimSize ), + aVDevMask.GetBitmap( + aEmptyPoint, + aAnimSize )))); + + // setup mtf dimensions and pref map mode (for + // simplicity, keep it all in pixel. the metafile + // renderer scales it down to (1, 1) box anyway) + pMtf->SetPrefMapMode( MapMode() ); + pMtf->SetPrefSize( aAnimSize ); + + // #115934# + // Take care of special value for MultiPage TIFFs. ATM these shall just + // show their first page for _quite_ some time. + sal_Int32 nWaitTime100thSeconds( rAnimBmp.nWait ); + if( ANIMATION_TIMEOUT_ON_CLICK == nWaitTime100thSeconds ) + { + // ATM the huge value would block the timer, so use a long + // time to show first page (whole day) + nWaitTime100thSeconds = 100 * 60 * 60 * 24; + } + + // There are animated GIFs with no WaitTime set. Take 1 sec, then. + if( nWaitTime100thSeconds == 0 ) + nWaitTime100thSeconds = 100; + + o_rFrames.push_back( MtfAnimationFrame( pMtf, + nWaitTime100thSeconds / 100.0 ) ); + } + + return !o_rFrames.empty(); +} + +bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle& o_rScrollRect, + ::basegfx::B2DRectangle& o_rPaintRect, + const GDIMetaFileSharedPtr& rMtf ) +{ + // extract bounds: scroll rect, paint rect + bool bScrollRectSet(false); + bool bPaintRectSet(false); + + for ( MetaAction * pCurrAct = rMtf->FirstAction(); + pCurrAct != 0; pCurrAct = rMtf->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("XTEXT") ) == COMPARE_EQUAL) + { + if (pAct->GetComment().CompareIgnoreCaseToAscii( + "XTEXT_SCROLLRECT" ) == COMPARE_EQUAL) { + o_rScrollRect = ::vcl::unotools::b2DRectangleFromRectangle( + *reinterpret_cast<Rectangle const *>( + pAct->GetData() ) ); + + bScrollRectSet = true; + } + else if (pAct->GetComment().CompareIgnoreCaseToAscii( + "XTEXT_PAINTRECT" ) == COMPARE_EQUAL) { + o_rPaintRect = ::vcl::unotools::b2DRectangleFromRectangle( + *reinterpret_cast<Rectangle const *>( + pAct->GetData() ) ); + + bPaintRectSet = true; + } + } + } + } + + return bScrollRectSet && bPaintRectSet; +} + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/gdimtftools.hxx b/slideshow/source/engine/shapes/gdimtftools.hxx new file mode 100644 index 000000000000..072880739e14 --- /dev/null +++ b/slideshow/source/engine/shapes/gdimtftools.hxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_GDIMTFTOOLS_HXX +#define INCLUDED_SLIDESHOW_GDIMTFTOOLS_HXX + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> + +#include <basegfx/range/b2drectangle.hxx> +#include <boost/shared_ptr.hpp> + +#include "tools.hxx" + +#include <vector> + +class MetaAction; +class GDIMetaFile; +class Graphic; + +// ----------------------------------------------------------------------------- + +namespace slideshow +{ + namespace internal + { + /// meta file loading specialities: + enum mtf_load_flags { + /// no flags + MTF_LOAD_NONE = 0, + /// annotate text actions with verbose comments, + /// denoting logical and physical text entities. + MTF_LOAD_VERBOSE_COMMENTS = 1, + /// the source of the metafile might be a foreign + /// application. The metafile is checked against unsupported + /// content, and, if necessary, returned as a pre-rendererd + /// bitmap. + MTF_LOAD_FOREIGN_SOURCE = 2, + /// retrieve a meta file for the page background only + MTF_LOAD_BACKGROUND_ONLY = 4, + /// retrieve the drawing layer scroll text metafile + MTF_LOAD_SCROLL_TEXT_MTF = 8 + }; + + // Animation info + // ============== + + struct MtfAnimationFrame + { + MtfAnimationFrame( const GDIMetaFileSharedPtr& rMtf, + double nDuration ) : + mpMtf( rMtf ), + mnDuration( nDuration ) + { + } + + /// Enables STL algos to be used for duration extraction + double getDuration() const + { + return mnDuration; + } + + GDIMetaFileSharedPtr mpMtf; + double mnDuration; + }; + + typedef ::std::vector< MtfAnimationFrame > VectorOfMtfAnimationFrames; + + + /** Retrieve a meta file for the given shape + + @param xShape + XShape to retrieve a metafile for. + + @param xContainingPage + The page that contains this shape. Needed for proper + import (currently, the UnoGraphicExporter needs this + information). + + @param o_rMtf + Metafile to extract shape content into + */ + bool getMetaFile( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent >& xSource, + const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XDrawPage >& xContainingPage, + GDIMetaFile& o_rMtf, + int mtfLoadFlags, + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XComponentContext >& rxContext ); + + /** Remove all text actions from the given metafile. + */ + void removeTextActions( GDIMetaFile& io_rMtf ); + + /** Gets the next action offset for iterating meta actions which is most + often returns 1. + */ + sal_Int32 getNextActionOffset( MetaAction * pCurrAct ); + + /** Extract a vector of animation frames from given Graphic. + + @param o_rFrames + Resulting vector of animated metafiles + + @param o_rLoopCount + Number of times the bitmap animation shall be repeated + + @param o_eCycleMode + Repeat mode (normal, or ping-pong mode) + + @param rGraphic + Input graphic object, to extract animations from + */ + bool getAnimationFromGraphic( VectorOfMtfAnimationFrames& o_rFrames, + ::std::size_t& o_rLoopCount, + CycleMode& o_eCycleMode, + const Graphic& rGraphic ); + + /** Retrieve scroll text animation rectangles from given metafile + + @return true, if both rectangles have been found, false + otherwise. + */ + bool getRectanglesFromScrollMtf( ::basegfx::B2DRectangle& o_rScrollRect, + ::basegfx::B2DRectangle& o_rPaintRect, + const GDIMetaFileSharedPtr& rMtf ); + } +} + +#endif /* INCLUDED_SLIDESHOW_GDIMTFTOOLS_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/intrinsicanimationactivity.cxx b/slideshow/source/engine/shapes/intrinsicanimationactivity.cxx new file mode 100644 index 000000000000..fb5179545821 --- /dev/null +++ b/slideshow/source/engine/shapes/intrinsicanimationactivity.cxx @@ -0,0 +1,293 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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" + +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <canvas/verbosetrace.hxx> + +#include "drawshapesubsetting.hxx" +#include "subsettableshapemanager.hxx" +#include "eventqueue.hxx" +#include "eventmultiplexer.hxx" +#include "intrinsicanimationactivity.hxx" +#include "intrinsicanimationeventhandler.hxx" + +#include <boost/noncopyable.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/weak_ptr.hpp> + +namespace slideshow +{ + namespace internal + { + /** Activity for intrinsic shape animations + + This is an Activity interface implementation for intrinsic + shape animations. Intrinsic shape animations are + animations directly within a shape, e.g. drawing layer + animations, or GIF animations. + */ + class IntrinsicAnimationActivity : public Activity, + public boost::enable_shared_from_this<IntrinsicAnimationActivity>, + private boost::noncopyable + { + public: + /** Create an IntrinsicAnimationActivity. + + @param rContext + Common slideshow objects + + @param rDrawShape + Shape to control the intrinsic animation for + + @param rWakeupEvent + Externally generated wakeup event, to set this + activity to sleep during inter-frame intervals. Must + come frome the outside, since wakeup event and this + object have mutual references to each other. + + @param rTimeouts + Vector of timeout values, to wait before the next + frame is shown. + */ + IntrinsicAnimationActivity( const SlideShowContext& rContext, + const DrawShapeSharedPtr& rDrawShape, + const WakeupEventSharedPtr& rWakeupEvent, + const ::std::vector<double>& rTimeouts, + ::std::size_t nNumLoops, + CycleMode eCycleMode ); + + virtual void dispose(); + virtual double calcTimeLag() const; + virtual bool perform(); + virtual bool isActive() const; + virtual void dequeued(); + virtual void end(); + + bool enableAnimations(); + + private: + SlideShowContext maContext; + boost::weak_ptr<DrawShape> mpDrawShape; + WakeupEventSharedPtr mpWakeupEvent; + IntrinsicAnimationEventHandlerSharedPtr mpListener; + ::std::vector<double> maTimeouts; + CycleMode meCycleMode; + ::std::size_t mnCurrIndex; + ::std::size_t mnNumLoops; + ::std::size_t mnLoopCount; + bool mbIsActive; + }; + + ////////////////////////////////////////////////////////////////////// + + class IntrinsicAnimationListener : public IntrinsicAnimationEventHandler, + private boost::noncopyable + { + public: + explicit IntrinsicAnimationListener( IntrinsicAnimationActivity& rActivity ) : + mrActivity( rActivity ) + {} + + private: + + virtual bool enableAnimations() { return mrActivity.enableAnimations(); } + virtual bool disableAnimations() { mrActivity.end(); return true; } + + IntrinsicAnimationActivity& mrActivity; + }; + + ////////////////////////////////////////////////////////////////////// + + IntrinsicAnimationActivity::IntrinsicAnimationActivity( const SlideShowContext& rContext, + const DrawShapeSharedPtr& rDrawShape, + const WakeupEventSharedPtr& rWakeupEvent, + const ::std::vector<double>& rTimeouts, + ::std::size_t nNumLoops, + CycleMode eCycleMode ) : + maContext( rContext ), + mpDrawShape( rDrawShape ), + mpWakeupEvent( rWakeupEvent ), + mpListener( new IntrinsicAnimationListener(*this) ), + maTimeouts( rTimeouts ), + meCycleMode( eCycleMode ), + mnCurrIndex(0), + mnNumLoops(nNumLoops), + mnLoopCount(0), + mbIsActive(false) + { + ENSURE_OR_THROW( rContext.mpSubsettableShapeManager, + "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Invalid shape manager" ); + ENSURE_OR_THROW( rDrawShape, + "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Invalid draw shape" ); + ENSURE_OR_THROW( rWakeupEvent, + "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Invalid wakeup event" ); + ENSURE_OR_THROW( !rTimeouts.empty(), + "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Empty timeout vector" ); + + maContext.mpSubsettableShapeManager->addIntrinsicAnimationHandler( + mpListener ); + } + + void IntrinsicAnimationActivity::dispose() + { + end(); + + if( mpWakeupEvent ) + mpWakeupEvent->dispose(); + + maContext.dispose(); + mpDrawShape.reset(); + mpWakeupEvent.reset(); + maTimeouts.clear(); + mnCurrIndex = 0; + + maContext.mpSubsettableShapeManager->removeIntrinsicAnimationHandler( + mpListener ); + } + + double IntrinsicAnimationActivity::calcTimeLag() const + { + return 0.0; + } + + bool IntrinsicAnimationActivity::perform() + { + if( !isActive() ) + return false; + + DrawShapeSharedPtr pDrawShape( mpDrawShape.lock() ); + if( !pDrawShape || !mpWakeupEvent ) + { + // event or draw shape vanished, no sense living on -> + // commit suicide. + dispose(); + return false; + } + + // mnNumLoops == 0 means infinite looping + if( mnNumLoops != 0 && + mnLoopCount >= mnNumLoops ) + { + // #i55294# After finishing the loops, display the first frame + pDrawShape->setIntrinsicAnimationFrame( 0 ); + maContext.mpSubsettableShapeManager->notifyShapeUpdate( pDrawShape ); + + end(); + + return false; + } + + ::std::size_t nNewIndex = 0; + const ::std::size_t nNumFrames(maTimeouts.size()); + switch( meCycleMode ) + { + case CYCLE_LOOP: + { + pDrawShape->setIntrinsicAnimationFrame( mnCurrIndex ); + + mpWakeupEvent->start(); + mpWakeupEvent->setNextTimeout( maTimeouts[mnCurrIndex] ); + + mnLoopCount += (mnCurrIndex + 1) / nNumFrames; + nNewIndex = (mnCurrIndex + 1) % nNumFrames; + break; + } + + case CYCLE_PINGPONGLOOP: + { + ::std::size_t nTrueIndex( mnCurrIndex < nNumFrames ? + mnCurrIndex : + 2*nNumFrames - mnCurrIndex - 1 ); + pDrawShape->setIntrinsicAnimationFrame( nTrueIndex ); + + mpWakeupEvent->start(); + mpWakeupEvent->setNextTimeout( maTimeouts[nTrueIndex] ); + + mnLoopCount += (mnCurrIndex + 1) / (2*nNumFrames); + nNewIndex = (mnCurrIndex + 1) % 2*nNumFrames; + break; + } + } + + maContext.mrEventQueue.addEvent( mpWakeupEvent ); + maContext.mpSubsettableShapeManager->notifyShapeUpdate( pDrawShape ); + mnCurrIndex = nNewIndex; + + return false; // don't reinsert, WakeupEvent will perform + // that after the given timeout + } + + bool IntrinsicAnimationActivity::isActive() const + { + return mbIsActive; + } + + void IntrinsicAnimationActivity::dequeued() + { + // not used here + } + + void IntrinsicAnimationActivity::end() + { + // there is no dedicated end state, just become inactive: + mbIsActive = false; + } + + bool IntrinsicAnimationActivity::enableAnimations() + { + mbIsActive = true; + return maContext.mrActivitiesQueue.addActivity( + shared_from_this() ); + } + + ////////////////////////////////////////////////////////////////////// + + ActivitySharedPtr createIntrinsicAnimationActivity( + const SlideShowContext& rContext, + const DrawShapeSharedPtr& rDrawShape, + const WakeupEventSharedPtr& rWakeupEvent, + const ::std::vector<double>& rTimeouts, + ::std::size_t nNumLoops, + CycleMode eCycleMode ) + { + return ActivitySharedPtr( + new IntrinsicAnimationActivity(rContext, + rDrawShape, + rWakeupEvent, + rTimeouts, + nNumLoops, + eCycleMode) ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/intrinsicanimationactivity.hxx b/slideshow/source/engine/shapes/intrinsicanimationactivity.hxx new file mode 100644 index 000000000000..a48ae8f1117e --- /dev/null +++ b/slideshow/source/engine/shapes/intrinsicanimationactivity.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_INTRINSICANIMATIONACTIVITY_HXX +#define INCLUDED_SLIDESHOW_INTRINSICANIMATIONACTIVITY_HXX + +#include "wakeupevent.hxx" +#include "activity.hxx" +#include "slideshowcontext.hxx" +#include "drawshape.hxx" +#include "tools.hxx" + +/* Definition of IntrinsicAnimationActivity class */ + +namespace slideshow +{ + namespace internal + { + /** Create an IntrinsicAnimationActivity. + + This is an Activity interface implementation for intrinsic + shape animations. Intrinsic shape animations are + animations directly within a shape, e.g. drawing layer + animations, or GIF animations. + + @param rContext + Common slideshow objects + + @param rDrawShape + Shape to control the intrinsic animation for + + @param rWakeupEvent + Externally generated wakeup event, to set this + activity to sleep during inter-frame intervals. Must + come frome the outside, since wakeup event and this + object have mutual references to each other. + + @param rTimeouts + Vector of timeout values, to wait before the next + frame is shown. + */ + ActivitySharedPtr createIntrinsicAnimationActivity( + const SlideShowContext& rContext, + const DrawShapeSharedPtr& rDrawShape, + const WakeupEventSharedPtr& rWakeupEvent, + const ::std::vector<double>& rTimeouts, + ::std::size_t nNumLoops, + CycleMode eCycleMode ); + } +} + +#endif /* INCLUDED_SLIDESHOW_INTRINSICANIMATIONACTIVITY_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/makefile.mk b/slideshow/source/engine/shapes/makefile.mk new file mode 100644 index 000000000000..1d40c165707c --- /dev/null +++ b/slideshow/source/engine/shapes/makefile.mk @@ -0,0 +1,60 @@ +#************************************************************************* +# +# 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. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=slideshow +TARGET=shapes +ENABLE_EXCEPTIONS=TRUE +PRJINC=..$/.. + +# --- Settings ----------------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Common ---------------------------------------------------------- + +SLOFILES = $(SLO)$/appletshape.obj \ + $(SLO)$/backgroundshape.obj \ + $(SLO)$/drawinglayeranimation.obj \ + $(SLO)$/drawshape.obj \ + $(SLO)$/drawshapesubsetting.obj \ + $(SLO)$/externalshapebase.obj \ + $(SLO)$/gdimtftools.obj \ + $(SLO)$/intrinsicanimationactivity.obj \ + $(SLO)$/mediashape.obj \ + $(SLO)$/shapeimporter.obj \ + $(SLO)$/viewappletshape.obj \ + $(SLO)$/viewbackgroundshape.obj \ + $(SLO)$/viewmediashape.obj \ + $(SLO)$/viewshape.obj + + +# ========================================================================== + +.INCLUDE : target.mk diff --git a/slideshow/source/engine/shapes/mediashape.cxx b/slideshow/source/engine/shapes/mediashape.cxx new file mode 100644 index 000000000000..9bcf66a030f6 --- /dev/null +++ b/slideshow/source/engine/shapes/mediashape.cxx @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <canvas/verbosetrace.hxx> +#include <canvas/canvastools.hxx> + +#include <com/sun/star/drawing/XShape.hpp> + +#include "mediashape.hxx" +#include "viewmediashape.hxx" +#include "externalshapebase.hxx" +#include "slideshowcontext.hxx" +#include "shape.hxx" +#include "tools.hxx" + +#include <boost/bind.hpp> +#include <algorithm> + + +using namespace ::com::sun::star; + + +namespace slideshow +{ + namespace internal + { + /** Represents a media shape. + + This implementation offers support for media shapes. + Such shapes need special treatment. + */ + class MediaShape : public ExternalShapeBase + { + public: + /** Create a shape for the given XShape for a media object + + @param xShape + The XShape to represent. + + @param nPrio + Externally-determined shape priority (used e.g. for + paint ordering). This number _must be_ unique! + */ + MediaShape( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + double nPrio, + const SlideShowContext& rContext ); // throw ShapeLoadFailedException; + + private: + + // View layer methods + //------------------------------------------------------------------ + + virtual void addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ); + virtual bool removeViewLayer( const ViewLayerSharedPtr& rNewLayer ); + virtual bool clearAllViewLayers(); + + + // ExternalShapeBase methods + //------------------------------------------------------------------ + + virtual bool implRender( const ::basegfx::B2DRange& rCurrBounds ) const; + virtual void implViewChanged( const UnoViewSharedPtr& rView ); + virtual void implViewsChanged(); + virtual bool implStartIntrinsicAnimation(); + virtual bool implEndIntrinsicAnimation(); + virtual bool implPauseIntrinsicAnimation(); + virtual bool implIsIntrinsicAnimationPlaying() const; + virtual void implSetIntrinsicAnimationTime(double); + + /// the list of active view shapes (one for each registered view layer) + typedef ::std::vector< ViewMediaShapeSharedPtr > ViewMediaShapeVector; + ViewMediaShapeVector maViewMediaShapes; + bool mbIsPlaying; + }; + + + MediaShape::MediaShape( const uno::Reference< drawing::XShape >& xShape, + double nPrio, + const SlideShowContext& rContext ) : + ExternalShapeBase( xShape, nPrio, rContext ), + maViewMediaShapes(), + mbIsPlaying(false) + { + } + + // --------------------------------------------------------------------- + + void MediaShape::implViewChanged( const UnoViewSharedPtr& rView ) + { + // determine ViewMediaShape that needs update + ViewMediaShapeVector::const_iterator aIter(maViewMediaShapes.begin()); + ViewMediaShapeVector::const_iterator const aEnd (maViewMediaShapes.end()); + while( aIter != aEnd ) + { + if( (*aIter)->getViewLayer()->isOnView(rView) ) + (*aIter)->resize(getBounds()); + + ++aIter; + } + } + + // --------------------------------------------------------------------- + + void MediaShape::implViewsChanged() + { + // resize all ViewShapes + ::std::for_each( maViewMediaShapes.begin(), + maViewMediaShapes.end(), + ::boost::bind( + &ViewMediaShape::resize, + _1, + ::boost::cref( getBounds())) ); + } + + // --------------------------------------------------------------------- + + void MediaShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer, + bool bRedrawLayer ) + { + maViewMediaShapes.push_back( + ViewMediaShapeSharedPtr( new ViewMediaShape( rNewLayer, + getXShape(), + mxComponentContext ))); + + // push new size to view shape + maViewMediaShapes.back()->resize( getBounds() ); + + // render the Shape on the newly added ViewLayer + if( bRedrawLayer ) + maViewMediaShapes.back()->render( getBounds() ); + } + + // --------------------------------------------------------------------- + + bool MediaShape::removeViewLayer( const ViewLayerSharedPtr& rLayer ) + { + const ViewMediaShapeVector::iterator aEnd( maViewMediaShapes.end() ); + + OSL_ENSURE( ::std::count_if(maViewMediaShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewMediaShape::getViewLayer, _1 ), + ::boost::cref( rLayer ) ) ) < 2, + "MediaShape::removeViewLayer(): Duplicate ViewLayer entries!" ); + + ViewMediaShapeVector::iterator aIter; + + if( (aIter=::std::remove_if( maViewMediaShapes.begin(), + aEnd, + ::boost::bind<bool>( + ::std::equal_to< ViewLayerSharedPtr >(), + ::boost::bind( &ViewMediaShape::getViewLayer, + _1 ), + ::boost::cref( rLayer ) ) )) == aEnd ) + { + // view layer seemingly was not added, failed + return false; + } + + // actually erase from container + maViewMediaShapes.erase( aIter, aEnd ); + + return true; + } + + // --------------------------------------------------------------------- + + bool MediaShape::clearAllViewLayers() + { + maViewMediaShapes.clear(); + return true; + } + + // --------------------------------------------------------------------- + + bool MediaShape::implRender( const ::basegfx::B2DRange& rCurrBounds ) const + { + // redraw all view shapes, by calling their update() method + if( ::std::count_if( maViewMediaShapes.begin(), + maViewMediaShapes.end(), + ::boost::bind<bool>( + ::boost::mem_fn( &ViewMediaShape::render ), + _1, + ::boost::cref( rCurrBounds ) ) ) + != static_cast<ViewMediaShapeVector::difference_type>(maViewMediaShapes.size()) ) + { + // at least one of the ViewShape::update() calls did return + // false - update failed on at least one ViewLayer + return false; + } + + return true; + } + + // --------------------------------------------------------------------- + + bool MediaShape::implStartIntrinsicAnimation() + { + ::std::for_each( maViewMediaShapes.begin(), + maViewMediaShapes.end(), + ::boost::mem_fn( &ViewMediaShape::startMedia ) ); + + mbIsPlaying = true; + + return true; + } + + // --------------------------------------------------------------------- + + bool MediaShape::implEndIntrinsicAnimation() + { + ::std::for_each( maViewMediaShapes.begin(), + maViewMediaShapes.end(), + ::boost::mem_fn( &ViewMediaShape::endMedia ) ); + + mbIsPlaying = false; + + return true; + } + + // --------------------------------------------------------------------- + + bool MediaShape::implPauseIntrinsicAnimation() + { + ::std::for_each( maViewMediaShapes.begin(), + maViewMediaShapes.end(), + ::boost::mem_fn( &ViewMediaShape::pauseMedia ) ); + + mbIsPlaying = false; + + return true; + } + + // --------------------------------------------------------------------- + + bool MediaShape::implIsIntrinsicAnimationPlaying() const + { + return mbIsPlaying; + } + + // --------------------------------------------------------------------- + + void MediaShape::implSetIntrinsicAnimationTime(double fTime) + { + ::std::for_each( maViewMediaShapes.begin(), + maViewMediaShapes.end(), + ::boost::bind( &ViewMediaShape::setMediaTime, + _1, boost::cref(fTime)) ); + } + + // --------------------------------------------------------------------- + + ShapeSharedPtr createMediaShape( + const uno::Reference< drawing::XShape >& xShape, + double nPrio, + const SlideShowContext& rContext) + { + boost::shared_ptr< MediaShape > pMediaShape( + new MediaShape(xShape, nPrio, rContext)); + + return pMediaShape; + } + + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/mediashape.hxx b/slideshow/source/engine/shapes/mediashape.hxx new file mode 100644 index 000000000000..babe30e8b122 --- /dev/null +++ b/slideshow/source/engine/shapes/mediashape.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_MEDIASHAPE_HXX +#define INCLUDED_SLIDESHOW_MEDIASHAPE_HXX + +#include <com/sun/star/uno/Reference.hxx> +#include <boost/shared_ptr.hpp> + + +namespace com { namespace sun { namespace star { namespace drawing +{ + class XShape; +} } } } + +namespace slideshow +{ + namespace internal + { + struct SlideShowContext; + class Shape; + + boost::shared_ptr<Shape> createMediaShape( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape, + double nPrio, + const SlideShowContext& rContext); + } +} + +#endif /* INCLUDED_SLIDESHOW_MEDIASHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/shapeimporter.cxx b/slideshow/source/engine/shapes/shapeimporter.cxx new file mode 100644 index 000000000000..0c8d6cbcf23e --- /dev/null +++ b/slideshow/source/engine/shapes/shapeimporter.cxx @@ -0,0 +1,672 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <vcl/cvtgrf.hxx> +#include <tools/urlobj.hxx> +#include <tools/stream.hxx> +#include <svtools/grfmgr.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/streamwrap.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <cppcanvas/polypolygon.hxx> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/drawing/ColorMode.hpp> +#include <com/sun/star/text/GraphicCrop.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/drawing/PointSequenceSequence.hpp> +#include <com/sun/star/drawing/PointSequence.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/drawing/XLayerSupplier.hpp> +#include <com/sun/star/drawing/XLayerManager.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> + +#include "drawshapesubsetting.hxx" +#include "drawshape.hxx" +#include "backgroundshape.hxx" +#include "mediashape.hxx" +#include "appletshape.hxx" +#include "shapeimporter.hxx" +#include "slideshowexceptions.hxx" +#include "gdimtftools.hxx" +#include "tools.hxx" +#include "slideshowcontext.hxx" + +#include <boost/shared_ptr.hpp> +#include <boost/scoped_ptr.hpp> + +using namespace com::sun::star; +using namespace ::comphelper; + +namespace slideshow { +namespace internal { + +namespace { + +bool importShapeGraphic( + GraphicObject & o_rGraphic, + uno::Reference<beans::XPropertySet> const& xPropSet ) +{ + rtl::OUString aURL; + if( !getPropertyValue( aURL, xPropSet, OUSTR("GraphicURL")) || + aURL.getLength() == 0 ) + { + // no or empty property - cannot import shape graphic + return false; + } + + rtl::OUString const aVndUrl( + RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.GraphicObject:" ) ); + sal_Int32 nIndex( aURL.indexOf( aVndUrl ) ); + + if(nIndex != -1) + { + // skip past the end of the "vnd..." prefix + nIndex += aVndUrl.getLength(); + + if(nIndex >= aURL.getLength()) + { + OSL_ENSURE( false, "ShapeImporter::importShape(): " + "embedded graphic has no graphic ID" ); + return false; + } + + // unique ID string found in URL, extract + // to separate string + rtl::OUString const aUniqueId( + aURL.copy( nIndex, aURL.getLength() - nIndex ) ); + + // TODO(T2): Creating a GraphicObject is not + // thread safe (internally calls VCL, and has + // unguarded internal singleton mpGlobalMgr) + + // fetch already loaded graphic from graphic manager. + ByteString const aOldString( static_cast<String>(aUniqueId), + RTL_TEXTENCODING_UTF8 ); + o_rGraphic = GraphicObject( aOldString ); + + + if( GRAPHIC_DEFAULT == o_rGraphic.GetType() + || GRAPHIC_NONE == o_rGraphic.GetType() ) + { + // even the GrfMgr does not seem to know this graphic + return false; + } + } + else + { + // no special string found, graphic must be + // external. Load via GraphicIm porter + INetURLObject aTmp( aURL ); + boost::scoped_ptr<SvStream> pGraphicStream( + utl::UcbStreamHelper::CreateStream( + aTmp.GetMainURL( INetURLObject::NO_DECODE ), + STREAM_READ ) ); + if( !pGraphicStream ) + { + OSL_ENSURE( false, "ShapeImporter::importShape(): " + "cannot create input stream for graphic" ); + return false; + } + + Graphic aTmpGraphic; + if( GraphicConverter::Import( + *pGraphicStream, aTmpGraphic ) != ERRCODE_NONE ) + { + OSL_ENSURE( false, "ShapeImporter::importShape(): " + "Failed to import shape graphic from given URL" ); + return false; + } + + o_rGraphic = GraphicObject( aTmpGraphic ); + } + return true; +} + +/** This shape implementation just acts as a dummy for the layermanager. + Its sole role is for hit test detection of group shapes. +*/ +class ShapeOfGroup : public Shape +{ +public: + ShapeOfGroup( ShapeSharedPtr const& pGroupShape, + uno::Reference<drawing::XShape> const& xShape, + uno::Reference<beans::XPropertySet> const& xPropSet, + double nPrio ); + + // Shape: + virtual uno::Reference<drawing::XShape> getXShape() const; + virtual void addViewLayer( ViewLayerSharedPtr const& pNewLayer, + bool bRedrawLayer ); + virtual bool removeViewLayer( ViewLayerSharedPtr const& pNewLayer ); + virtual bool clearAllViewLayers(); + virtual bool update() const; + virtual bool render() const; + virtual bool isContentChanged() const; + virtual basegfx::B2DRectangle getBounds() const; + virtual basegfx::B2DRectangle getDomBounds() const; + virtual basegfx::B2DRectangle getUpdateArea() const; + virtual bool isVisible() const; + virtual double getPriority() const; + virtual bool isBackgroundDetached() const; + +private: + ShapeSharedPtr const mpGroupShape; + uno::Reference<drawing::XShape> const mxShape; + double const mnPrio; + basegfx::B2DPoint maPosOffset; + double mnWidth; + double mnHeight; +}; + +ShapeOfGroup::ShapeOfGroup( ShapeSharedPtr const& pGroupShape, + uno::Reference<drawing::XShape> const& xShape, + uno::Reference<beans::XPropertySet> const& xPropSet, + double nPrio ) : + mpGroupShape(pGroupShape), + mxShape(xShape), + mnPrio(nPrio) +{ + // read bound rect + uno::Any const aTmpRect_( xPropSet->getPropertyValue( OUSTR("BoundRect") )); + awt::Rectangle const aTmpRect( aTmpRect_.get<awt::Rectangle>() ); + basegfx::B2DRectangle const groupPosSize( pGroupShape->getBounds() ); + maPosOffset = basegfx::B2DPoint( aTmpRect.X - groupPosSize.getMinX(), + aTmpRect.Y - groupPosSize.getMinY() ); + mnWidth = aTmpRect.Width; + mnHeight = aTmpRect.Height; +} + +uno::Reference<drawing::XShape> ShapeOfGroup::getXShape() const +{ + return mxShape; +} + +void ShapeOfGroup::addViewLayer( ViewLayerSharedPtr const& /*pNewLayer*/, + bool /*bRedrawLayer*/ ) +{ +} + +bool ShapeOfGroup::removeViewLayer( ViewLayerSharedPtr const& /*pNewLayer*/ ) +{ + return true; +} + +bool ShapeOfGroup::clearAllViewLayers() +{ + return true; +} + +bool ShapeOfGroup::update() const +{ + return true; +} + +bool ShapeOfGroup::render() const +{ + return true; +} + +bool ShapeOfGroup::isContentChanged() const +{ + return false; +} + +basegfx::B2DRectangle ShapeOfGroup::getBounds() const +{ + basegfx::B2DRectangle const groupPosSize( mpGroupShape->getBounds() ); + double const posX = (groupPosSize.getMinX() + maPosOffset.getX()); + double const posY = (groupPosSize.getMinY() + maPosOffset.getY()); + return basegfx::B2DRectangle( posX, posY, posX + mnWidth, posY + mnHeight ); +} + +basegfx::B2DRectangle ShapeOfGroup::getDomBounds() const +{ + return getBounds(); +} + +basegfx::B2DRectangle ShapeOfGroup::getUpdateArea() const +{ + return getBounds(); +} + +bool ShapeOfGroup::isVisible() const +{ + return mpGroupShape->isVisible(); +} + +double ShapeOfGroup::getPriority() const +{ + return mnPrio; +} + +bool ShapeOfGroup::isBackgroundDetached() const +{ + return false; +} + +} // anon namespace + +ShapeSharedPtr ShapeImporter::createShape( + uno::Reference<drawing::XShape> const& xCurrShape, + uno::Reference<beans::XPropertySet> const& xPropSet, + rtl::OUString const& shapeType ) const +{ + if( shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("com.sun.star.drawing.MediaShape") )) + { + // Media shape (video etc.). This is a special object + return createMediaShape(xCurrShape, + mnAscendingPrio, + mrContext); + } + else if( shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("com.sun.star.drawing.PluginShape") )) + { + // PropertyValues to copy from XShape to plugin + static const char* aPropertyValues[] = + { + "PluginURL", + "PluginMimeType", + "PluginCommands" + }; + + // (Netscape)Plugin shape. This is a special object + return createAppletShape( xCurrShape, + mnAscendingPrio, + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.comp.sfx2.PluginObject" )), + aPropertyValues, + sizeof(aPropertyValues)/sizeof(*aPropertyValues), + mrContext ); + } + else if( shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("com.sun.star.drawing.AppletShape") )) + { + // PropertyValues to copy from XShape to applet + static const char* aPropertyValues[] = + { + "AppletCodeBase", + "AppletName", + "AppletCode", + "AppletCommands", + "AppletIsScript" + }; + + // (Java)Applet shape. This is a special object + return createAppletShape( xCurrShape, + mnAscendingPrio, + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.comp.sfx2.AppletObject" )), + aPropertyValues, + sizeof(aPropertyValues)/sizeof(*aPropertyValues), + mrContext ); + } + else if( shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("com.sun.star.drawing.OLE2Shape") )) + { + // #i46224# Mark OLE shapes as foreign content - scan them for + // unsupported actions, and fallback to bitmap, if necessary + return DrawShape::create( xCurrShape, + mxPage, + mnAscendingPrio, + true, + mrContext ); + } + else if( shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM( + "com.sun.star.drawing.GraphicObjectShape") )) + { + GraphicObject aGraphicObject; + + // to get hold of GIF animations, inspect Graphic + // objects more thoroughly (the plain-jane shape + // metafile of course would only contain the first + // animation frame) + if( !importShapeGraphic( aGraphicObject, xPropSet ) ) + return ShapeSharedPtr(); // error loading graphic - + // #142147# no placeholders in + // slideshow + + if( !aGraphicObject.IsAnimated() ) + { + // no animation - simply utilize plain draw shape import + + // import shape as bitmap - either its a bitmap + // anyway, or its a metafile, which currently the + // metafile renderer might not display correctly. + return DrawShape::create( xCurrShape, + mxPage, + mnAscendingPrio, + true, + mrContext ); + } + + + // now extract relevant shape attributes via API + // --------------------------------------------- + + drawing::ColorMode eColorMode( drawing::ColorMode_STANDARD ); + sal_Int16 nLuminance(0); + sal_Int16 nContrast(0); + sal_Int16 nRed(0); + sal_Int16 nGreen(0); + sal_Int16 nBlue(0); + double nGamma(1.0); + sal_Int16 nTransparency(0); + sal_Int32 nRotation(0); + + getPropertyValue( eColorMode, xPropSet, OUSTR("GraphicColorMode") ); + getPropertyValue( nLuminance, xPropSet, OUSTR("AdjustLuminance") ); + getPropertyValue( nContrast, xPropSet, OUSTR("AdjustContrast") ); + getPropertyValue( nRed, xPropSet, OUSTR("AdjustRed") ); + getPropertyValue( nGreen, xPropSet, OUSTR("AdjustGreen") ); + getPropertyValue( nBlue, xPropSet, OUSTR("AdjustBlue") ); + getPropertyValue( nGamma, xPropSet, OUSTR("Gamma") ); + getPropertyValue( nTransparency, xPropSet, OUSTR("Transparency") ); + getPropertyValue( nRotation, xPropSet, OUSTR("RotateAngle") ); + + GraphicAttr aGraphAttrs; + aGraphAttrs.SetDrawMode( (GraphicDrawMode)eColorMode ); + aGraphAttrs.SetLuminance( nLuminance ); + aGraphAttrs.SetContrast( nContrast ); + aGraphAttrs.SetChannelR( nRed ); + aGraphAttrs.SetChannelG( nGreen ); + aGraphAttrs.SetChannelB( nBlue ); + aGraphAttrs.SetGamma( nGamma ); + aGraphAttrs.SetTransparency( static_cast<BYTE>(nTransparency) ); + aGraphAttrs.SetRotation( static_cast<USHORT>(nRotation*10) ); + + text::GraphicCrop aGraphCrop; + if( getPropertyValue( aGraphCrop, xPropSet, OUSTR("GraphicCrop") )) + { + aGraphAttrs.SetCrop( aGraphCrop.Left, + aGraphCrop.Top, + aGraphCrop.Right, + aGraphCrop.Bottom ); + } + + // fetch readily transformed and color-modified + // graphic + // --------------------------------------------- + + Graphic aGraphic( + aGraphicObject.GetTransformedGraphic( + aGraphicObject.GetPrefSize(), + aGraphicObject.GetPrefMapMode(), + aGraphAttrs ) ); + + return DrawShape::create( xCurrShape, + mxPage, + mnAscendingPrio, + aGraphic, + mrContext ); + } + else + { + return DrawShape::create( xCurrShape, + mxPage, + mnAscendingPrio, + false, + mrContext ); + } +} + +bool ShapeImporter::isSkip( + uno::Reference<beans::XPropertySet> const& xPropSet, + rtl::OUString const& shapeType, + uno::Reference< drawing::XLayer> const& xLayer ) +{ + // skip empty presentation objects: + bool bEmpty = false; + if( getPropertyValue( bEmpty, + xPropSet, + OUSTR("IsEmptyPresentationObject")) && + bEmpty ) + { + return true; + } + + //skip shapes which corresponds to annotations + if(xLayer.is()) + { + rtl::OUString layerName; + uno::Reference<beans::XPropertySet> xPropLayerSet( + xLayer, uno::UNO_QUERY ); + const uno::Any& a(xPropLayerSet->getPropertyValue(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Name"))) ); + bool const bRet = (a >>= layerName); + if(bRet) + { + if( layerName.equals(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("DrawnInSlideshow")))) + { + //Transform shapes into PolyPolygons + importPolygons(xPropSet); + + return true; + } + } + } + + // don't export presentation placeholders on masterpage + // they can be non empty when user edits the default texts + if(mbConvertingMasterPage) + { + if(shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("com.sun.star.presentation." + "TitleTextShape") ) || + shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("com.sun.star.presentation." + "OutlinerShape") )) + { + return true; + } + } + return false; +} + + +void ShapeImporter::importPolygons(uno::Reference<beans::XPropertySet> const& xPropSet) { + + drawing::PointSequenceSequence aRetval; + sal_Int32 nLineColor=0; + double fLineWidth; + getPropertyValue( aRetval, xPropSet, OUSTR("PolyPolygon") ); + getPropertyValue( nLineColor, xPropSet, OUSTR("LineColor") ); + getPropertyValue( fLineWidth, xPropSet, OUSTR("LineWidth") ); + + drawing::PointSequence* pOuterSequence = aRetval.getArray(); + awt::Point* pInnerSequence = pOuterSequence->getArray(); + + ::basegfx::B2DPolygon aPoly; + basegfx::B2DPoint aPoint; + for( sal_Int32 nCurrPoly=0; nCurrPoly<pOuterSequence->getLength(); ++nCurrPoly, ++pInnerSequence ) + { + aPoint.setX((*pInnerSequence).X); + aPoint.setY((*pInnerSequence).Y); + aPoly.append( aPoint ); + } + UnoViewVector::const_iterator aIter=(mrContext.mrViewContainer).begin(); + UnoViewVector::const_iterator aEnd=(mrContext.mrViewContainer).end(); + while(aIter != aEnd) + { + ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( + ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( (*aIter)->getCanvas(), + aPoly ) ); + if( pPolyPoly ) + { + pPolyPoly->setRGBALineColor( unoColor2RGBColor( nLineColor ).getIntegerColor() ); + pPolyPoly->setStrokeWidth(fLineWidth); + pPolyPoly->draw(); + maPolygons.push_back(pPolyPoly); + } + aIter++; + } +} + +ShapeSharedPtr ShapeImporter::importBackgroundShape() // throw (ShapeLoadFailedException) +{ + if( maShapesStack.empty() ) + throw ShapeLoadFailedException(); + + XShapesEntry& rTop = maShapesStack.top(); + ShapeSharedPtr pBgShape( + createBackgroundShape(mxPage, + uno::Reference<drawing::XDrawPage>( + rTop.mxShapes, + uno::UNO_QUERY_THROW), + mrContext) ); + mnAscendingPrio += 1.0; + + return pBgShape; +} + +ShapeSharedPtr ShapeImporter::importShape() // throw (ShapeLoadFailedException) +{ + ShapeSharedPtr pRet; + bool bIsGroupShape = false; + + while( !maShapesStack.empty() && !pRet ) + { + XShapesEntry& rTop = maShapesStack.top(); + if( rTop.mnPos < rTop.mnCount ) + { + uno::Reference<drawing::XShape> const xCurrShape( + rTop.mxShapes->getByIndex( rTop.mnPos ), uno::UNO_QUERY ); + ++rTop.mnPos; + uno::Reference<beans::XPropertySet> xPropSet( + xCurrShape, uno::UNO_QUERY ); + if( !xPropSet.is() ) + { + // we definitely need the properties of + // the shape here. This will also fail, + // if getByIndex did not return a valid + // shape + throw ShapeLoadFailedException(); + } + + //Retrieve the layer for the current shape + uno::Reference< drawing::XLayer > xDrawnInSlideshow; + + uno::Reference< drawing::XLayerSupplier > xLayerSupplier(mxPagesSupplier, uno::UNO_QUERY); + if(xLayerSupplier.is()) + { + uno::Reference< container::XNameAccess > xNameAccess = xLayerSupplier->getLayerManager(); + + uno::Reference< drawing::XLayerManager > xLayerManager(xNameAccess, uno::UNO_QUERY); + + xDrawnInSlideshow = xLayerManager->getLayerForShape(xCurrShape); + } + + rtl::OUString const shapeType( xCurrShape->getShapeType()); + + // is this shape presentation-invisible? + if( !isSkip(xPropSet, shapeType, xDrawnInSlideshow) ) + { + bIsGroupShape = shapeType.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM( + "com.sun.star.drawing.GroupShape") ); + + if( rTop.mpGroupShape ) // in group particle mode? + { + pRet.reset( new ShapeOfGroup( + rTop.mpGroupShape /* container shape */, + xCurrShape, xPropSet, + mnAscendingPrio ) ); + } + else + { + pRet = createShape( xCurrShape, xPropSet, shapeType ); + } + mnAscendingPrio += 1.0; + } + } + if( rTop.mnPos >= rTop.mnCount ) + { + // group or top-level shapes finished: + maShapesStack.pop(); + } + if( bIsGroupShape && pRet ) + { + // push new group on the stack: group traversal + maShapesStack.push( XShapesEntry( pRet ) ); + } + } + + return pRet; +} + +bool ShapeImporter::isImportDone() const +{ + return maShapesStack.empty(); +} + +PolyPolygonVector ShapeImporter::getPolygons() +{ + return maPolygons; +} + +ShapeImporter::ShapeImporter( uno::Reference<drawing::XDrawPage> const& xPage, + uno::Reference<drawing::XDrawPage> const& xActualPage, + uno::Reference<drawing::XDrawPagesSupplier> const& xPagesSupplier, + const SlideShowContext& rContext, + sal_Int32 nOrdNumStart, + bool bConvertingMasterPage ) : + mxPage( xActualPage ), +#ifdef ENABLE_PRESENTER_EXTRA_UI + mxPagesSupplier( xPagesSupplier ), +#else + mxPagesSupplier( NULL ), +#endif + mrContext( rContext ), + maPolygons(), + maShapesStack(), + mnAscendingPrio( nOrdNumStart ), + mbConvertingMasterPage( bConvertingMasterPage ) +{ +#ifndef ENABLE_PRESENTER_EXTRA_UI + (void)xPagesSupplier; +#endif + uno::Reference<drawing::XShapes> const xShapes( + xPage, uno::UNO_QUERY_THROW ); + maShapesStack.push( XShapesEntry(xShapes) ); +} + +} // namespace internal +} // namespace presentation + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewappletshape.cxx b/slideshow/source/engine/shapes/viewappletshape.cxx new file mode 100644 index 000000000000..f22927ddd520 --- /dev/null +++ b/slideshow/source/engine/shapes/viewappletshape.cxx @@ -0,0 +1,303 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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/verbosetrace.hxx> +#include <tools/diagnose_ex.h> +#include <canvas/debug.hxx> + +#include <comphelper/anytostring.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b2irange.hxx> +#include <basegfx/tools/canvastools.hxx> + +#include <cppcanvas/spritecanvas.hxx> +#include <canvas/canvastools.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/awt/WindowDescriptor.hpp> +#include <com/sun/star/awt/XToolkit.hpp> +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XSynchronousFrameLoader.hpp> + +#include "viewappletshape.hxx" +#include "tools.hxx" + + +using namespace ::com::sun::star; + +namespace slideshow +{ + namespace internal + { + ViewAppletShape::ViewAppletShape( const ViewLayerSharedPtr& rViewLayer, + const uno::Reference< drawing::XShape >& rxShape, + const ::rtl::OUString& rServiceName, + const char** pPropCopyTable, + sal_Size nNumPropEntries, + const uno::Reference< uno::XComponentContext >& rxContext ) : + mpViewLayer( rViewLayer ), + mxViewer(), + mxFrame(), + mxComponentContext( rxContext ) + { + ENSURE_OR_THROW( rxShape.is(), "ViewAppletShape::ViewAppletShape(): Invalid Shape" ); + ENSURE_OR_THROW( mpViewLayer, "ViewAppletShape::ViewAppletShape(): Invalid View" ); + ENSURE_OR_THROW( mpViewLayer->getCanvas(), "ViewAppletShape::ViewAppletShape(): Invalid ViewLayer canvas" ); + ENSURE_OR_THROW( mxComponentContext.is(), "ViewAppletShape::ViewAppletShape(): Invalid component context" ); + + uno::Reference<lang::XMultiComponentFactory> xFactory( + mxComponentContext->getServiceManager(), + uno::UNO_QUERY_THROW ); + + mxViewer.set( xFactory->createInstanceWithContext( rServiceName, + mxComponentContext), + uno::UNO_QUERY_THROW ); + + uno::Reference< beans::XPropertySet > xShapePropSet( rxShape, + uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > mxViewerPropSet( mxViewer, + uno::UNO_QUERY_THROW ); + + // copy shape properties to applet viewer + ::rtl::OUString aPropName; + for( sal_Size i=0; i<nNumPropEntries; ++i ) + { + aPropName = ::rtl::OUString::createFromAscii( pPropCopyTable[i] ); + mxViewerPropSet->setPropertyValue( aPropName, + xShapePropSet->getPropertyValue( + aPropName )); + } + } + + // --------------------------------------------------------------------- + + ViewAppletShape::~ViewAppletShape() + { + try + { + endApplet(); + } + catch (uno::Exception &) + { + OSL_ENSURE( false, rtl::OUStringToOString( + comphelper::anyToString( + cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + + // --------------------------------------------------------------------- + + ViewLayerSharedPtr ViewAppletShape::getViewLayer() const + { + return mpViewLayer; + } + + // --------------------------------------------------------------------- + + bool ViewAppletShape::startApplet( const ::basegfx::B2DRectangle& rBounds ) + { + ENSURE_OR_RETURN_FALSE( mpViewLayer && mpViewLayer->getCanvas() && mpViewLayer->getCanvas()->getUNOCanvas().is(), + "ViewAppletShape::startApplet(): Invalid or disposed view" ); + try + { + ::cppcanvas::CanvasSharedPtr pCanvas = mpViewLayer->getCanvas(); + + uno::Reference< beans::XPropertySet > xPropSet( pCanvas->getUNOCanvas()->getDevice(), + uno::UNO_QUERY_THROW ); + + uno::Reference< awt::XWindow2 > xParentWindow( + xPropSet->getPropertyValue( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "Window" ))), + uno::UNO_QUERY_THROW ); + + uno::Reference<lang::XMultiComponentFactory> xFactory( + mxComponentContext->getServiceManager() ); + + if( xFactory.is() ) + { + // create an awt window to contain the applet + // ========================================== + + uno::Reference< awt::XToolkit > xToolkit( + xFactory->createInstanceWithContext( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.Toolkit" )), + mxComponentContext ), + uno::UNO_QUERY_THROW ); + + awt::WindowDescriptor aOwnWinDescriptor( awt::WindowClass_SIMPLE, + ::rtl::OUString(), + uno::Reference< awt::XWindowPeer >(xParentWindow, + uno::UNO_QUERY_THROW), + 0, + awt::Rectangle(), + awt::WindowAttribute::SHOW + | awt::VclWindowPeerAttribute::CLIPCHILDREN ); + + uno::Reference< awt::XWindowPeer > xNewWinPeer( + xToolkit->createWindow( aOwnWinDescriptor )); + uno::Reference< awt::XWindow > xOwnWindow( xNewWinPeer, + uno::UNO_QUERY_THROW ); + + + // create a frame, and load the applet into it + // =========================================== + + mxFrame.set( + xFactory->createInstanceWithContext( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Frame" )), + mxComponentContext ), + uno::UNO_QUERY_THROW ); + + mxFrame->initialize( xOwnWindow ); + + uno::Reference < frame::XSynchronousFrameLoader > xLoader( mxViewer, + uno::UNO_QUERY_THROW ); + xLoader->load( uno::Sequence < beans::PropertyValue >(), + mxFrame ); + + + // resize surrounding window and applet to current shape size + // ========================================================== + + ::basegfx::B2DRange aTmpRange; + ::canvas::tools::calcTransformedRectBounds( aTmpRange, + rBounds, + mpViewLayer->getTransformation() ); + const ::basegfx::B2IRange& rPixelBounds( + ::basegfx::unotools::b2ISurroundingRangeFromB2DRange( aTmpRange )); + + uno::Reference< awt::XWindow > xSurroundingWindow( mxFrame->getContainerWindow() ); + if( xSurroundingWindow.is() ) + xSurroundingWindow->setPosSize( static_cast<sal_Int32>(rPixelBounds.getMinX()), + static_cast<sal_Int32>(rPixelBounds.getMinY()), + static_cast<sal_Int32>(rPixelBounds.getWidth()), + static_cast<sal_Int32>(rPixelBounds.getHeight()), + awt::PosSize::POSSIZE ); + + uno::Reference< awt::XWindow > xAppletWindow( mxFrame->getComponentWindow() ); + if( xAppletWindow.is() ) + xAppletWindow->setPosSize( 0, 0, + static_cast<sal_Int32>(rPixelBounds.getWidth()), + static_cast<sal_Int32>(rPixelBounds.getHeight()), + awt::PosSize::POSSIZE ); + } + } + catch (uno::Exception &) + { + return false; + } + + return true; + } + + // --------------------------------------------------------------------- + + void ViewAppletShape::endApplet() + { + uno::Reference<util::XCloseable> xCloseable( + mxFrame, + uno::UNO_QUERY ); + + if( xCloseable.is() ) + { + xCloseable->close( sal_True ); + mxFrame.clear(); + } + } + + // --------------------------------------------------------------------- + + bool ViewAppletShape::render( const ::basegfx::B2DRectangle& rBounds ) const + { + ::cppcanvas::CanvasSharedPtr pCanvas = mpViewLayer->getCanvas(); + + if( !pCanvas ) + return false; + + if( !mxFrame.is() ) + { + // fill the shape background with black + fillRect( pCanvas, + rBounds, + 0xFFFFFFFFU ); + } + + return true; + } + + bool ViewAppletShape::resize( const ::basegfx::B2DRectangle& rBounds ) const + { + if( !mxFrame.is() ) + return false; + + ::basegfx::B2DRange aTmpRange; + ::canvas::tools::calcTransformedRectBounds( aTmpRange, + rBounds, + mpViewLayer->getTransformation() ); + const ::basegfx::B2IRange& rPixelBounds( + ::basegfx::unotools::b2ISurroundingRangeFromB2DRange( aTmpRange )); + + uno::Reference< awt::XWindow > xFrameWindow( mxFrame->getContainerWindow() ); + if( xFrameWindow.is() ) + xFrameWindow->setPosSize( static_cast<sal_Int32>(rPixelBounds.getMinX()), + static_cast<sal_Int32>(rPixelBounds.getMinY()), + static_cast<sal_Int32>(rPixelBounds.getWidth()), + static_cast<sal_Int32>(rPixelBounds.getHeight()), + awt::PosSize::POSSIZE ); + + uno::Reference< awt::XWindow > xAppletWindow( mxFrame->getComponentWindow() ); + if( xAppletWindow.is() ) + xAppletWindow->setPosSize( 0, 0, + static_cast<sal_Int32>(rPixelBounds.getWidth()), + static_cast<sal_Int32>(rPixelBounds.getHeight()), + awt::PosSize::POSSIZE ); + + return true; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewappletshape.hxx b/slideshow/source/engine/shapes/viewappletshape.hxx new file mode 100644 index 000000000000..29e81c4e2bc5 --- /dev/null +++ b/slideshow/source/engine/shapes/viewappletshape.hxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_VIEWAPPLETSHAPE_HXX +#define INCLUDED_SLIDESHOW_VIEWAPPLETSHAPE_HXX + +#include <basegfx/range/b2drectangle.hxx> +#include <com/sun/star/awt/Point.hpp> + +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +#include "viewlayer.hxx" + +namespace com { namespace sun { namespace star { +namespace frame { + class XSynchronousFrameLoader; + class XFrame; +} +namespace uno { + class XComponentContext; +} +namespace drawing { + class XShape; +}}}} + +namespace slideshow +{ + namespace internal + { + /** This class is the viewable representation of a draw + document's applet object, associated to a specific View + + The class is able to render the associated applet on View + implementations. + */ + class ViewAppletShape : private boost::noncopyable + { + public: + /** Create a ViewAppletShape for the given View + + @param rViewLayer + The associated View object. + + @param rxShape + The associated Shape + + @param rServiceName + The service name to use, when actually creating the + viewer component + + @param pPropCopyTable + Table of plain ASCII property names, to copy from + xShape to applet. + + @param nNumPropEntries + Number of property table entries (in pPropCopyTable) + */ + ViewAppletShape( const ViewLayerSharedPtr& rViewLayer, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& rxShape, + const ::rtl::OUString& rServiceName, + const char** pPropCopyTable, + sal_Size nNumPropEntries, + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XComponentContext >& rxContext ); + + /** destroy the object + */ + virtual ~ViewAppletShape(); + + /** Query the associated view layer of this shape + */ + ViewLayerSharedPtr getViewLayer() const; + + // animation methods + //------------------------------------------------------------------ + + /** Notify the ViewShape that an animation starts now + + This method enters animation mode on the associate + target view. The shape can be animated in parallel on + different views. + + @param rBounds + The current applet shape bounds + + @return whether the mode change finished successfully. + */ + bool startApplet( const ::basegfx::B2DRectangle& rBounds ); + + /** Notify the ViewShape that it is no longer animated + + This methods ends animation mode on the associate + target view + */ + void endApplet(); + + // render methods + //------------------------------------------------------------------ + + /** Render the ViewShape + + This method renders the ViewAppletShape on the associated view. + + @param rBounds + The current applet shape bounds + + @return whether the rendering finished successfully. + */ + bool render( const ::basegfx::B2DRectangle& rBounds ) const; + + /** Resize the ViewShape + + This method resizes the ViewAppletShape on the + associated view. It does not render. + + @param rBounds + The current applet shape bounds + + @return whether the resize finished successfully. + */ + bool resize( const ::basegfx::B2DRectangle& rBounds ) const; + + private: + + ViewLayerSharedPtr mpViewLayer; + + /// the actual viewer component for this applet + ::com::sun::star::uno::Reference< + ::com::sun::star::frame::XSynchronousFrameLoader> mxViewer; + + /// the frame containing the applet + ::com::sun::star::uno::Reference< + ::com::sun::star::frame::XFrame> mxFrame; + ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XComponentContext> mxComponentContext; + }; + + typedef ::boost::shared_ptr< ViewAppletShape > ViewAppletShapeSharedPtr; + + } +} + +#endif /* INCLUDED_SLIDESHOW_VIEWAPPLETSHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewbackgroundshape.cxx b/slideshow/source/engine/shapes/viewbackgroundshape.cxx new file mode 100644 index 000000000000..e8b0930e1940 --- /dev/null +++ b/slideshow/source/engine/shapes/viewbackgroundshape.cxx @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 "viewbackgroundshape.hxx" +#include "tools.hxx" + +#include <rtl/logfile.hxx> +#include <rtl/math.hxx> + +#include <comphelper/anytostring.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +#include <com/sun/star/rendering/XCanvas.hpp> + +#include <canvas/verbosetrace.hxx> +#include <canvas/canvastools.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <cppcanvas/renderer.hxx> +#include <cppcanvas/bitmap.hxx> + +using namespace ::com::sun::star; + + +namespace slideshow +{ + namespace internal + { + + bool ViewBackgroundShape::prefetch( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewBackgroundShape::prefetch()" ); + ENSURE_OR_RETURN_FALSE( rMtf, + "ViewBackgroundShape::prefetch(): no valid metafile!" ); + + const ::basegfx::B2DHomMatrix& rCanvasTransform( + mpViewLayer->getTransformation() ); + + if( !mxBitmap.is() || + rMtf != mpLastMtf || + rCanvasTransform != maLastTransformation ) + { + // buffered bitmap is invalid, re-create + + // determine transformed page bounds + ::basegfx::B2DRectangle aTmpRect; + ::canvas::tools::calcTransformedRectBounds( aTmpRect, + maBounds, + rCanvasTransform ); + + // determine pixel size of bitmap (choose it one pixel + // larger, as polygon rendering takes one pixel more + // to the right and to the bottom) + const ::basegfx::B2ISize aBmpSizePixel( + ::basegfx::fround( aTmpRect.getRange().getX() + 1), + ::basegfx::fround( aTmpRect.getRange().getY() + 1) ); + + // create a bitmap of appropriate size + ::cppcanvas::BitmapSharedPtr pBitmap( + ::cppcanvas::BaseGfxFactory::getInstance().createBitmap( + rDestinationCanvas, + aBmpSizePixel ) ); + + ENSURE_OR_THROW( pBitmap, + "ViewBackgroundShape::prefetch(): Cannot create background bitmap" ); + + ::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas( pBitmap->getBitmapCanvas() ); + + ENSURE_OR_THROW( pBitmapCanvas, + "ViewBackgroundShape::prefetch(): Cannot create background bitmap canvas" ); + + // clear bitmap + initSlideBackground( pBitmapCanvas, + aBmpSizePixel ); + + // apply linear part of destination canvas transformation (linear means in this context: + // transformation without any translational components) + ::basegfx::B2DHomMatrix aLinearTransform( rCanvasTransform ); + aLinearTransform.set( 0, 2, 0.0 ); + aLinearTransform.set( 1, 2, 0.0 ); + pBitmapCanvas->setTransformation( aLinearTransform ); + + const basegfx::B2DHomMatrix aShapeTransform(basegfx::tools::createScaleTranslateB2DHomMatrix( + maBounds.getWidth(), maBounds.getHeight(), + maBounds.getMinX(), maBounds.getMinY())); + + ::cppcanvas::RendererSharedPtr pRenderer( + ::cppcanvas::VCLFactory::getInstance().createRenderer( + pBitmapCanvas, + *rMtf.get(), + ::cppcanvas::Renderer::Parameters() ) ); + + ENSURE_OR_RETURN_FALSE( pRenderer, + "ViewBackgroundShape::prefetch(): Could not create Renderer" ); + + pRenderer->setTransformation( aShapeTransform ); + pRenderer->draw(); + + mxBitmap = pBitmap->getUNOBitmap(); + } + + mpLastMtf = rMtf; + maLastTransformation = rCanvasTransform; + + return mxBitmap.is(); + } + + ViewBackgroundShape::ViewBackgroundShape( const ViewLayerSharedPtr& rViewLayer, + const ::basegfx::B2DRectangle& rShapeBounds ) : + mpViewLayer( rViewLayer ), + mxBitmap(), + mpLastMtf(), + maLastTransformation(), + maBounds( rShapeBounds ) + { + ENSURE_OR_THROW( mpViewLayer, "ViewBackgroundShape::ViewBackgroundShape(): Invalid View" ); + ENSURE_OR_THROW( mpViewLayer->getCanvas(), "ViewBackgroundShape::ViewBackgroundShape(): Invalid ViewLayer canvas" ); + } + + ViewLayerSharedPtr ViewBackgroundShape::getViewLayer() const + { + return mpViewLayer; + } + + bool ViewBackgroundShape::render( const GDIMetaFileSharedPtr& rMtf ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewBackgroundShape::draw()" ); + + const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas( mpViewLayer->getCanvas() ); + + if( !prefetch( rDestinationCanvas, rMtf ) ) + return false; + + ENSURE_OR_RETURN_FALSE( mxBitmap.is(), + "ViewBackgroundShape::draw(): Invalid background bitmap" ); + + ::basegfx::B2DHomMatrix aTransform( mpViewLayer->getTransformation() ); + + // invert the linear part of the view transformation + // (i.e. the view transformation without translational + // components), to be able to leave the canvas + // transformation intact (would otherwise destroy possible + // clippings, as the clip polygon is relative to the view + // coordinate system). + aTransform.set(0,2, 0.0 ); + aTransform.set(1,2, 0.0 ); + aTransform.invert(); + + rendering::RenderState aRenderState; + ::canvas::tools::initRenderState( aRenderState ); + + ::canvas::tools::setRenderStateTransform( aRenderState, aTransform ); + + try + { + rDestinationCanvas->getUNOCanvas()->drawBitmap( mxBitmap, + rDestinationCanvas->getViewState(), + aRenderState ); + } + catch( uno::Exception& ) + { + OSL_ENSURE( false, + rtl::OUStringToOString( + comphelper::anyToString( cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + + return false; + } + + return true; + } + + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewbackgroundshape.hxx b/slideshow/source/engine/shapes/viewbackgroundshape.hxx new file mode 100644 index 000000000000..da7beb7b03be --- /dev/null +++ b/slideshow/source/engine/shapes/viewbackgroundshape.hxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_VIEWBACKGROUNDSHAPE_HXX +#define INCLUDED_SLIDESHOW_VIEWBACKGROUNDSHAPE_HXX + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/rendering/XBitmap.hpp> + +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <cppcanvas/spritecanvas.hxx> + +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +#include "gdimtftools.hxx" +#include "viewlayer.hxx" + + +namespace slideshow +{ + namespace internal + { + /** This class is the viewable representation of a draw + document's background, associated to a specific View + + The class is able to render the associated background on + View implementations. + */ + class ViewBackgroundShape : private boost::noncopyable + { + public: + /** Create a ViewBackgroundShape for the given View + + @param rView + The associated View object. + + @param rShapeBounds + Bounds of the background shape, in document coordinate + system. + */ + ViewBackgroundShape( const ViewLayerSharedPtr& rViewLayer, + const ::basegfx::B2DRectangle& rShapeBounds ); + + /** Query the associated view layer of this shape + */ + ViewLayerSharedPtr getViewLayer() const; + + bool render( const GDIMetaFileSharedPtr& rMtf ) const; + + private: + /** Prefetch bitmap for given canvas + */ + bool prefetch( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf ) const; + + /** The view layer this object is part of. + */ + ViewLayerSharedPtr mpViewLayer; + + /// Generated content bitmap, already with correct output size + mutable ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XBitmap > mxBitmap; + + /// The last metafile a render object was generated for + mutable GDIMetaFileSharedPtr mpLastMtf; + + /// The canvas, mpRenderer is associated with + mutable ::basegfx::B2DHomMatrix maLastTransformation; + + const ::basegfx::B2DRectangle maBounds; + }; + + typedef ::boost::shared_ptr< ViewBackgroundShape > ViewBackgroundShapeSharedPtr; + + } +} + +#endif /* INCLUDED_SLIDESHOW_VIEWBACKGROUNDSHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewmediashape.cxx b/slideshow/source/engine/shapes/viewmediashape.cxx new file mode 100644 index 000000000000..e861b789e561 --- /dev/null +++ b/slideshow/source/engine/shapes/viewmediashape.cxx @@ -0,0 +1,575 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <math.h> + +#include <comphelper/anytostring.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <vcl/window.hxx> +#include <vcl/javachild.hxx> +#include <vcl/salbtype.hxx> +#ifdef GSTREAMER +#include <vcl/sysdata.hxx> +#endif + +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/range/b2irange.hxx> +#include <canvas/canvastools.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <avmedia/mediawindow.hxx> + +#include <com/sun/star/media/XManager.hpp> +#include <com/sun/star/media/XPlayer.hpp> +#include <com/sun/star/media/XPlayerWindow.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/lang/XComponent.hdl> + +#include "viewmediashape.hxx" +#include "mediashape.hxx" +#include "tools.hxx" +#include "unoview.hxx" + +using namespace ::com::sun::star; + +namespace slideshow +{ + namespace internal + { + ViewMediaShape::ViewMediaShape( const ViewLayerSharedPtr& rViewLayer, + const uno::Reference< drawing::XShape >& rxShape, + const uno::Reference< uno::XComponentContext >& rxContext ) : + mpViewLayer( rViewLayer ), + mpMediaWindow(), + maWindowOffset( 0, 0 ), + maBounds(), + mxShape( rxShape ), + mxPlayer(), + mxPlayerWindow(), + mxComponentContext( rxContext ), + mbIsSoundEnabled(true) + { + ENSURE_OR_THROW( mxShape.is(), "ViewMediaShape::ViewMediaShape(): Invalid Shape" ); + ENSURE_OR_THROW( mpViewLayer, "ViewMediaShape::ViewMediaShape(): Invalid View" ); + ENSURE_OR_THROW( mpViewLayer->getCanvas(), "ViewMediaShape::ViewMediaShape(): Invalid ViewLayer canvas" ); + ENSURE_OR_THROW( mxComponentContext.is(), "ViewMediaShape::ViewMediaShape(): Invalid component context" ); + + UnoViewSharedPtr pUnoView (::boost::dynamic_pointer_cast<UnoView>(rViewLayer)); + if (pUnoView) + { + mbIsSoundEnabled = pUnoView->isSoundEnabled(); + } + } + + // --------------------------------------------------------------------- + + ViewMediaShape::~ViewMediaShape() + { + try + { + endMedia(); + } + catch (uno::Exception &) + { + OSL_ENSURE( false, rtl::OUStringToOString( + comphelper::anyToString( + cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + + // --------------------------------------------------------------------- + + ViewLayerSharedPtr ViewMediaShape::getViewLayer() const + { + return mpViewLayer; + } + + // --------------------------------------------------------------------- + + bool ViewMediaShape::startMedia() + { + if( !mxPlayer.is() ) + implInitialize( maBounds ); + + if( mxPlayer.is() && ( mxPlayer->getDuration() > 0.0 ) ) + mxPlayer->start(); + + return true; + } + + // --------------------------------------------------------------------- + + void ViewMediaShape::endMedia() + { + // shutdown player window + if( mxPlayerWindow.is() ) + { + uno::Reference< lang::XComponent > xComponent( mxPlayerWindow, uno::UNO_QUERY ); + + if( xComponent.is() ) + xComponent->dispose(); + + mxPlayerWindow.clear(); + } + +#ifdef GSTREAMER + mpMediaWindow = ::std::auto_ptr< SystemChildWindow >(); +#else + mpMediaWindow = ::std::auto_ptr< JavaChildWindow >(); +#endif + + // shutdown player + if( mxPlayer.is() ) + { + mxPlayer->stop(); + + uno::Reference< lang::XComponent > xComponent( mxPlayer, uno::UNO_QUERY ); + + if( xComponent.is() ) + xComponent->dispose(); + + mxPlayer.clear(); + } + } + + // --------------------------------------------------------------------- + + void ViewMediaShape::pauseMedia() + { + if( mxPlayer.is() && ( mxPlayer->getDuration() > 0.0 ) ) + mxPlayer->stop(); + } + + // --------------------------------------------------------------------- + + void ViewMediaShape::setMediaTime(double fTime) + { + if( mxPlayer.is() && ( mxPlayer->getDuration() > 0.0 ) ) + mxPlayer->setMediaTime(fTime); + } + + // --------------------------------------------------------------------- + + bool ViewMediaShape::render( const ::basegfx::B2DRectangle& rBounds ) const + { + ::cppcanvas::CanvasSharedPtr pCanvas = mpViewLayer->getCanvas();; + + if( !pCanvas ) + return false; + + if( !mpMediaWindow.get() && !mxPlayerWindow.is() ) + { + // fill the shape background with black + fillRect( pCanvas, + rBounds, + 0x000000FFU ); + } + + return true; + } + + bool ViewMediaShape::resize( const ::basegfx::B2DRectangle& rNewBounds ) const + { + maBounds = rNewBounds; + + ::cppcanvas::CanvasSharedPtr pCanvas = mpViewLayer->getCanvas();; + + if( !pCanvas ) + return false; + + if( !mxPlayerWindow.is() ) + return true; + + uno::Reference< beans::XPropertySet > xPropSet( pCanvas->getUNOCanvas()->getDevice(), + uno::UNO_QUERY ); + + uno::Reference< awt::XWindow > xParentWindow; + if( xPropSet.is() && + getPropertyValue( xParentWindow, + xPropSet, + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "Window" ))) ) + { + const awt::Rectangle aRect( xParentWindow->getPosSize() ); + + maWindowOffset.X = aRect.X; + maWindowOffset.Y = aRect.Y; + } + + ::basegfx::B2DRange aTmpRange; + ::canvas::tools::calcTransformedRectBounds( aTmpRange, + rNewBounds, + mpViewLayer->getTransformation() ); + const ::basegfx::B2IRange& rRangePix( + ::basegfx::unotools::b2ISurroundingRangeFromB2DRange( aTmpRange )); + + mxPlayerWindow->setEnable( !rRangePix.isEmpty() ); + + if( rRangePix.isEmpty() ) + return true; + + const Point aPosPixel( rRangePix.getMinX() + maWindowOffset.X, + rRangePix.getMinY() + maWindowOffset.Y ); + const Size aSizePixel( rRangePix.getMaxX() - rRangePix.getMinX(), + rRangePix.getMaxY() - rRangePix.getMinY() ); + + if( mpMediaWindow.get() ) + { + mpMediaWindow->SetPosSizePixel( aPosPixel, aSizePixel ); + mxPlayerWindow->setPosSize( 0, 0, + aSizePixel.Width(), aSizePixel.Height(), + 0 ); + } + else + { + mxPlayerWindow->setPosSize( aPosPixel.X(), aPosPixel.Y(), + aSizePixel.Width(), aSizePixel.Height(), + 0 ); + } + + return true; + } + + // --------------------------------------------------------------------- + + bool ViewMediaShape::implInitialize( const ::basegfx::B2DRectangle& rBounds ) + { + if( !mxPlayer.is() && mxShape.is() ) + { + ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), + "ViewMediaShape::update(): Invalid layer canvas" ); + + uno::Reference< rendering::XCanvas > xCanvas( mpViewLayer->getCanvas()->getUNOCanvas() ); + + if( xCanvas.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet; + ::rtl::OUString aURL; + + try + { + xPropSet.set( mxShape, uno::UNO_QUERY ); + + // create Player + if( xPropSet.is() && + ( xPropSet->getPropertyValue( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MediaURL" ) ) ) >>=aURL ) ) + { + implInitializeMediaPlayer( aURL ); + } + + // create visible object + uno::Sequence< uno::Any > aDeviceParams; + + if( ::canvas::tools::getDeviceInfo( xCanvas, aDeviceParams ).getLength() > 1 ) + { + ::rtl::OUString aImplName; + + aDeviceParams[ 0 ] >>= aImplName; + + if( aImplName.endsWithIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM("VCL") ) || aImplName.endsWithIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM("Cairo") ) ) + { + implInitializeVCLBasedPlayerWindow( rBounds, aDeviceParams ); + } + else if( aImplName.endsWithIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM("DX")) || + aImplName.endsWithIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM("DX9"))) + { + implInitializeDXBasedPlayerWindow( rBounds, aDeviceParams ); + } + } + + // set player properties + implSetMediaProperties( xPropSet ); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( uno::Exception& ) + { + OSL_ENSURE( false, + rtl::OUStringToOString( + comphelper::anyToString( cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + } + + return mxPlayer.is() || mxPlayerWindow.is(); + } + + // --------------------------------------------------------------------- + + void ViewMediaShape::implSetMediaProperties( const uno::Reference< beans::XPropertySet >& rxProps ) + { + if( mxPlayer.is() ) + { + mxPlayer->setMediaTime( 0.0 ); + + if( rxProps.is() ) + { + sal_Bool bLoop( false ); + getPropertyValue( bLoop, + rxProps, + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Loop" ))); + mxPlayer->setPlaybackLoop( bLoop ); + + sal_Bool bMute( false ); + getPropertyValue( bMute, + rxProps, + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Mute" ))); + mxPlayer->setMute( bMute || !mbIsSoundEnabled); + + sal_Int16 nVolumeDB(0); + getPropertyValue( nVolumeDB, + rxProps, + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VolumeDB" ))); + mxPlayer->setVolumeDB( nVolumeDB ); + + if( mxPlayerWindow.is() ) + { + media::ZoomLevel eZoom(media::ZoomLevel_FIT_TO_WINDOW); + getPropertyValue( eZoom, + rxProps, + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Zoom" ))); + mxPlayerWindow->setZoomLevel( eZoom ); + } + } + } + } + + // --------------------------------------------------------------------- + + void ViewMediaShape::implInitializeMediaPlayer( const ::rtl::OUString& rMediaURL ) + { + if( !mxPlayer.is() ) + { + try + { + if( rMediaURL.getLength() ) + { + mxPlayer.set( avmedia::MediaWindow::createPlayer( rMediaURL ), + uno::UNO_QUERY ); + } + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( const uno::Exception& ) + { + throw lang::NoSupportException( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "No video support for ") ) + rMediaURL, + uno::Reference<uno::XInterface>() ); + } + } + } + + // --------------------------------------------------------------------- + + bool ViewMediaShape::implInitializeVCLBasedPlayerWindow( const ::basegfx::B2DRectangle& rBounds, + const uno::Sequence< uno::Any >& rVCLDeviceParams) + { + OSL_TRACE( "ViewMediaShape::implInitializeVCLBasedPlayerWindow" ); + if( !mpMediaWindow.get() && !rBounds.isEmpty() ) + { + try + { + sal_Int64 aVal=0; + + rVCLDeviceParams[ 1 ] >>= aVal; + + Window* pWindow = reinterpret_cast< Window* >( aVal ); + + if( pWindow ) + { + ::basegfx::B2DRange aTmpRange; + ::canvas::tools::calcTransformedRectBounds( aTmpRange, + rBounds, + mpViewLayer->getTransformation() ); + const ::basegfx::B2IRange& rRangePix( + ::basegfx::unotools::b2ISurroundingRangeFromB2DRange( aTmpRange )); + + if( !rRangePix.isEmpty() ) + { + uno::Sequence< uno::Any > aArgs( 3 ); + awt::Rectangle aAWTRect( rRangePix.getMinX(), + rRangePix.getMinY(), + rRangePix.getMaxX() - rRangePix.getMinX(), + rRangePix.getMaxY() - rRangePix.getMinY() ); + +#ifdef GSTREAMER + OSL_TRACE( "created sys child window for viewmediashape" ); + mpMediaWindow = ::std::auto_ptr< SystemChildWindow >( new SystemChildWindow( pWindow, WB_CLIPCHILDREN ) ); +#else + mpMediaWindow = ::std::auto_ptr< JavaChildWindow >( new JavaChildWindow( pWindow, WB_CLIPCHILDREN ) ); +#endif + mpMediaWindow->SetBackground( Color( COL_BLACK ) ); + mpMediaWindow->SetPosSizePixel( Point( aAWTRect.X, + aAWTRect.Y ), + Size( aAWTRect.Width, + aAWTRect.Height )); + mpMediaWindow->SetParentClipMode( PARENTCLIPMODE_NOCLIP ); + mpMediaWindow->EnableEraseBackground( FALSE ); + mpMediaWindow->EnablePaint( FALSE ); + mpMediaWindow->SetForwardKey( TRUE ); + mpMediaWindow->SetMouseTransparent( TRUE ); + mpMediaWindow->Show(); + + if( mxPlayer.is() ) + { +#ifndef GSTREAMER + aArgs[ 0 ] = uno::makeAny( + sal::static_int_cast<sal_IntPtr>( + mpMediaWindow->getParentWindowHandleForJava()) ); +#else + aArgs[ 0 ] = uno::makeAny ( (sal_Int32) 0 ); +#endif + aAWTRect.X = aAWTRect.Y = 0; + aArgs[ 1 ] = uno::makeAny( aAWTRect ); +#ifdef GSTREAMER + const SystemEnvData *pSystemData = mpMediaWindow->GetSystemData(); + OSL_TRACE( "xwindow id: %ld", pSystemData->aWindow ); + aArgs[ 2 ] = uno::makeAny( pSystemData->aWindow ); +#endif + + mxPlayerWindow.set( mxPlayer->createPlayerWindow( aArgs ) ); + + if( mxPlayerWindow.is() ) + { + mxPlayerWindow->setVisible( true ); + mxPlayerWindow->setEnable( true ); + } + } + } + } + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( uno::Exception& ) + { + OSL_ENSURE( false, + rtl::OUStringToOString( + comphelper::anyToString( cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + + return mxPlayerWindow.is(); + } + + // --------------------------------------------------------------------- + + bool ViewMediaShape::implInitializeDXBasedPlayerWindow( const ::basegfx::B2DRectangle& rBounds, + const uno::Sequence< uno::Any >& rDXDeviceParams ) + { + if( !mxPlayerWindow.is() ) + { + try + { + if( rDXDeviceParams.getLength() == 2 ) + { + sal_Int64 aWNDVal=0; + + rDXDeviceParams[ 1 ] >>= aWNDVal; + + if( aWNDVal ) + { + ::basegfx::B2DRange aTmpRange; + ::canvas::tools::calcTransformedRectBounds( aTmpRange, + rBounds, + mpViewLayer->getTransformation() ); + const ::basegfx::B2IRange& rRangePix( + ::basegfx::unotools::b2ISurroundingRangeFromB2DRange( aTmpRange )); + + if( !rRangePix.isEmpty() ) + { + uno::Sequence< uno::Any > aArgs( 2 ); + awt::Rectangle aAWTRect( rRangePix.getMinX() + maWindowOffset.X, + rRangePix.getMinY() + maWindowOffset.Y, + rRangePix.getMaxX() - rRangePix.getMinX(), + rRangePix.getMaxY() - rRangePix.getMinY() ); + + if( mxPlayer.is() ) + { + aArgs[ 0 ] = uno::makeAny( + sal::static_int_cast<sal_Int32>( + aWNDVal) ); + aArgs[ 1 ] = uno::makeAny( aAWTRect ); + + mxPlayerWindow.set( mxPlayer->createPlayerWindow( aArgs ) ); + } + } + } + } + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( uno::Exception& ) + { + OSL_ENSURE( false, + rtl::OUStringToOString( + comphelper::anyToString( cppu::getCaughtException() ), + RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + + return mxPlayerWindow.is(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewmediashape.hxx b/slideshow/source/engine/shapes/viewmediashape.hxx new file mode 100644 index 000000000000..4815f2dc9e6a --- /dev/null +++ b/slideshow/source/engine/shapes/viewmediashape.hxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_VIEWMEDIASHAPE_HXX +#define INCLUDED_SLIDESHOW_VIEWMEDIASHAPE_HXX + +#include <basegfx/range/b2drectangle.hxx> +#include <com/sun/star/awt/Point.hpp> + +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +#include "viewlayer.hxx" + +class JavaChildWindow; +class SystemChildWindow; + +namespace com { namespace sun { namespace star { namespace drawing { + class XShape; +} +namespace media { + class XPlayer; + class XPlayerWindow; +} +namespace uno { + class XComponentContext; +} +namespace beans{ + class XPropertySet; +} } } } + +namespace slideshow +{ + namespace internal + { + /** This class is the viewable representation of a draw + document's media object, associated to a specific View + + The class is able to render the associated media shape on + View implementations. + */ + class ViewMediaShape : private boost::noncopyable + { + public: + /** Create a ViewMediaShape for the given View + + @param rView + The associated View object. + */ + ViewMediaShape( const ViewLayerSharedPtr& rViewLayer, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& rxShape, + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XComponentContext >& rxContext ); + + /** destroy the object + */ + virtual ~ViewMediaShape(); + + /** Query the associated view layer of this shape + */ + ViewLayerSharedPtr getViewLayer() const; + + // animation methods + //------------------------------------------------------------------ + + /** Notify the ViewShape that an animation starts now + + This method enters animation mode on the associate + target view. The shape can be animated in parallel on + different views. + + @return whether the mode change finished successfully. + */ + bool startMedia(); + + /** Notify the ViewShape that it is no longer animated + + This methods ends animation mode on the associate + target view + */ + void endMedia(); + + /** Notify the ViewShape that it should pause playback + + This methods pauses animation on the associate + target view. The content stays visible (for video) + */ + void pauseMedia(); + + /** Set current time of media. + + @param fTime + Local media time that should now be presented, in seconds. + */ + void setMediaTime(double fTime); + + // render methods + //------------------------------------------------------------------ + + /** Render the ViewShape + + This method renders the ViewMediaShape on the associated view. + + @param rBounds + The current media shape bounds + + @return whether the rendering finished successfully. + */ + bool render( const ::basegfx::B2DRectangle& rBounds ) const; + + /** Resize the ViewShape + + This method updates the ViewMediaShape size on the + associated view. It does not render. + + @param rBounds + The current media shape bounds + + @return whether the resize finished successfully. + */ + bool resize( const ::basegfx::B2DRectangle& rNewBounds ) const; + + private: + + bool implInitialize( const ::basegfx::B2DRectangle& rBounds ); + void implSetMediaProperties( const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >& rxProps ); + void implInitializeMediaPlayer( const ::rtl::OUString& rMediaURL ); + bool implInitializeVCLBasedPlayerWindow( const ::basegfx::B2DRectangle& rBounds, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& rVCLDeviceParams ); + bool implInitializeDXBasedPlayerWindow( const ::basegfx::B2DRectangle& rBounds, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& rDXDeviceParams ); + + ViewLayerSharedPtr mpViewLayer; +#ifdef GSTREAMER + ::std::auto_ptr< SystemChildWindow > mpMediaWindow; +#else + ::std::auto_ptr< JavaChildWindow > mpMediaWindow; +#endif + mutable ::com::sun::star::awt::Point maWindowOffset; + mutable ::basegfx::B2DRectangle maBounds; + + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape > mxShape; + ::com::sun::star::uno::Reference< ::com::sun::star::media::XPlayer > mxPlayer; + ::com::sun::star::uno::Reference< ::com::sun::star::media::XPlayerWindow > mxPlayerWindow; + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext> mxComponentContext; + bool mbIsSoundEnabled; + }; + + typedef ::boost::shared_ptr< ViewMediaShape > ViewMediaShapeSharedPtr; + + } +} + +#endif /* INCLUDED_SLIDESHOW_VIEWMEDIASHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewshape.cxx b/slideshow/source/engine/shapes/viewshape.cxx new file mode 100644 index 000000000000..20b85756f389 --- /dev/null +++ b/slideshow/source/engine/shapes/viewshape.cxx @@ -0,0 +1,901 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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 <math.h> + +#include <rtl/logfile.hxx> +#include <rtl/math.hxx> + +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/rendering/XIntegerBitmap.hpp> +#include <com/sun/star/rendering/PanoseLetterForm.hpp> +#include <com/sun/star/awt/FontSlant.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/anytostring.hxx> + +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +#include <canvas/verbosetrace.hxx> +#include <canvas/canvastools.hxx> +#include <cppcanvas/vclfactory.hxx> +#include <cppcanvas/basegfxfactory.hxx> + +#include "viewshape.hxx" +#include "tools.hxx" + +#include <boost/bind.hpp> + + +using namespace ::com::sun::star; + +namespace slideshow +{ + namespace internal + { + + // TODO(F2): Provide sensible setup for mtf-related attributes (fill mode, + // char rotation etc.). Do that via mtf argument at this object + + bool ViewShape::prefetch( RendererCacheEntry& io_rCacheEntry, + const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ShapeAttributeLayerSharedPtr& rAttr ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::prefetch()" ); + ENSURE_OR_RETURN_FALSE( rMtf, + "ViewShape::prefetch(): no valid metafile!" ); + + if( rMtf != io_rCacheEntry.mpMtf || + rDestinationCanvas != io_rCacheEntry.getDestinationCanvas() ) + { + // buffered renderer invalid, re-create + ::cppcanvas::Renderer::Parameters aParms; + + // rendering attribute override parameter struct. For + // every valid attribute, the corresponding struct + // member is filled, which in the metafile renderer + // forces rendering with the given attribute. + if( rAttr ) + { + if( rAttr->isFillColorValid() ) + { + // convert RGBColor to RGBA32 integer. Note + // that getIntegerColor() also truncates + // out-of-range values appropriately + aParms.maFillColor = + rAttr->getFillColor().getIntegerColor(); + } + if( rAttr->isLineColorValid() ) + { + // convert RGBColor to RGBA32 integer. Note + // that getIntegerColor() also truncates + // out-of-range values appropriately + aParms.maLineColor = + rAttr->getLineColor().getIntegerColor(); + } + if( rAttr->isCharColorValid() ) + { + // convert RGBColor to RGBA32 integer. Note + // that getIntegerColor() also truncates + // out-of-range values appropriately + aParms.maTextColor = + rAttr->getCharColor().getIntegerColor(); + } + if( rAttr->isDimColorValid() ) + { + // convert RGBColor to RGBA32 integer. Note + // that getIntegerColor() also truncates + // out-of-range values appropriately + + // dim color overrides all other colors + aParms.maFillColor = + aParms.maLineColor = + aParms.maTextColor = + rAttr->getDimColor().getIntegerColor(); + } + if( rAttr->isFontFamilyValid() ) + { + aParms.maFontName = + rAttr->getFontFamily(); + } + if( rAttr->isCharScaleValid() ) + { + ::basegfx::B2DHomMatrix aMatrix; + + // enlarge text by given scale factor. Do that + // with the middle of the shape as the center + // of scaling. + aMatrix.translate( -0.5, -0.5 ); + aMatrix.scale( rAttr->getCharScale(), + rAttr->getCharScale() ); + aMatrix.translate( 0.5, 0.5 ); + + aParms.maTextTransformation = aMatrix; + } + if( rAttr->isCharWeightValid() ) + { + aParms.maFontWeight = + static_cast< sal_Int8 >( + ::basegfx::fround( + ::std::max( 0.0, + ::std::min( 11.0, + rAttr->getCharWeight() / 20.0 ) ) ) ); + } + if( rAttr->isCharPostureValid() ) + { + aParms.maFontLetterForm = + rAttr->getCharPosture() == awt::FontSlant_NONE ? + rendering::PanoseLetterForm::ANYTHING : + rendering::PanoseLetterForm::OBLIQUE_CONTACT; + } + if( rAttr->isUnderlineModeValid() ) + { + aParms.maFontUnderline = + rAttr->getUnderlineMode(); + } + } + + io_rCacheEntry.mpRenderer = ::cppcanvas::VCLFactory::getInstance().createRenderer( rDestinationCanvas, + *rMtf.get(), + aParms ); + + io_rCacheEntry.mpMtf = rMtf; + io_rCacheEntry.mpDestinationCanvas = rDestinationCanvas; + + // also invalidate alpha compositing bitmap (created + // new renderer, which possibly generates different + // output). Do NOT invalidate, if we're incidentally + // rendering INTO it. + if( rDestinationCanvas != io_rCacheEntry.mpLastBitmapCanvas ) + { + io_rCacheEntry.mpLastBitmapCanvas.reset(); + io_rCacheEntry.mpLastBitmap.reset(); + } + } + + return io_rCacheEntry.mpRenderer; + } + + bool ViewShape::draw( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ShapeAttributeLayerSharedPtr& rAttr, + const ::basegfx::B2DHomMatrix& rTransform, + const ::basegfx::B2DPolyPolygon* pClip, + const VectorOfDocTreeNodes& rSubsets ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::draw()" ); + + ::cppcanvas::RendererSharedPtr pRenderer( + getRenderer( rDestinationCanvas, rMtf, rAttr ) ); + + ENSURE_OR_RETURN_FALSE( pRenderer, "ViewShape::draw(): Invalid renderer" ); + + pRenderer->setTransformation( rTransform ); +#if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 + rendering::RenderState aRenderState; + ::canvas::tools::initRenderState(aRenderState); + ::canvas::tools::setRenderStateTransform(aRenderState, + rTransform); + aRenderState.DeviceColor.realloc(4); + aRenderState.DeviceColor[0] = 1.0; + aRenderState.DeviceColor[1] = 0.0; + aRenderState.DeviceColor[2] = 0.0; + aRenderState.DeviceColor[3] = 1.0; + + try + { + rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(0.0,0.0), + geometry::RealPoint2D(1.0,1.0), + rDestinationCanvas->getViewState(), + aRenderState ); + rDestinationCanvas->getUNOCanvas()->drawLine( geometry::RealPoint2D(1.0,0.0), + geometry::RealPoint2D(0.0,1.0), + rDestinationCanvas->getViewState(), + aRenderState ); + } + catch( uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } +#endif + if( pClip ) + pRenderer->setClip( *pClip ); + else + pRenderer->setClip(); + + if( rSubsets.empty() ) + { + return pRenderer->draw(); + } + else + { + // render subsets of whole metafile + // -------------------------------- + + bool bRet(true); + VectorOfDocTreeNodes::const_iterator aIter( rSubsets.begin() ); + const VectorOfDocTreeNodes::const_iterator aEnd ( rSubsets.end() ); + while( aIter != aEnd ) + { + if( !pRenderer->drawSubset( aIter->getStartIndex(), + aIter->getEndIndex() ) ) + bRet = false; + + ++aIter; + } + + return bRet; + } + } + + namespace + { + /// Convert untransformed shape update area to device pixel. + ::basegfx::B2DRectangle shapeArea2AreaPixel( const ::basegfx::B2DHomMatrix& rCanvasTransformation, + const ::basegfx::B2DRectangle& rUntransformedArea ) + { + // convert area to pixel, and add anti-aliasing border + + // TODO(P1): Should the view transform some + // day contain rotation/shear, transforming + // the original bounds with the total + // transformation might result in smaller + // overall bounds. + + ::basegfx::B2DRectangle aBoundsPixel; + ::canvas::tools::calcTransformedRectBounds( aBoundsPixel, + rUntransformedArea, + rCanvasTransformation ); + + // add antialiasing border around the shape (AA + // touches pixel _outside_ the nominal bound rect) + aBoundsPixel.grow( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE ); + + return aBoundsPixel; + } + + /// Convert shape unit rect to device pixel. + ::basegfx::B2DRectangle calcUpdateAreaPixel( const ::basegfx::B2DRectangle& rUnitBounds, + const ::basegfx::B2DHomMatrix& rShapeTransformation, + const ::basegfx::B2DHomMatrix& rCanvasTransformation, + const ShapeAttributeLayerSharedPtr& pAttr ) + { + // calc update area for whole shape (including + // character scaling) + return shapeArea2AreaPixel( rCanvasTransformation, + getShapeUpdateArea( rUnitBounds, + rShapeTransformation, + pAttr ) ); + } + } + + bool ViewShape::renderSprite( const ViewLayerSharedPtr& rViewLayer, + const GDIMetaFileSharedPtr& rMtf, + const ::basegfx::B2DRectangle& rOrigBounds, + const ::basegfx::B2DRectangle& rBounds, + const ::basegfx::B2DRectangle& rUnitBounds, + int nUpdateFlags, + const ShapeAttributeLayerSharedPtr& pAttr, + const VectorOfDocTreeNodes& rSubsets, + double nPrio, + bool bIsVisible ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::renderSprite()" ); + + // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape, + // in that all the common setup steps here are refactored to Shape (would then + // have to be performed only _once_ per Shape paint). + + if( !bIsVisible || + rUnitBounds.isEmpty() || + rOrigBounds.isEmpty() || + rBounds.isEmpty() ) + { + // shape is invisible or has zero size, no need to + // update anything. + if( mpSprite ) + mpSprite->hide(); + + return true; + } + + + // calc sprite position, size and content transformation + // ===================================================== + + // the shape transformation for a sprite is always a + // simple scale-up to the nominal shape size. Everything + // else is handled via the sprite transformation + ::basegfx::B2DHomMatrix aNonTranslationalShapeTransformation; + aNonTranslationalShapeTransformation.scale( rOrigBounds.getWidth(), + rOrigBounds.getHeight() ); + ::basegfx::B2DHomMatrix aShapeTransformation( aNonTranslationalShapeTransformation ); + aShapeTransformation.translate( rOrigBounds.getMinX(), + rOrigBounds.getMinY() ); + + const ::basegfx::B2DHomMatrix& rCanvasTransform( + rViewLayer->getSpriteTransformation() ); + + // area actually needed for the sprite + const ::basegfx::B2DRectangle& rSpriteBoundsPixel( + calcUpdateAreaPixel( rUnitBounds, + aShapeTransformation, + rCanvasTransform, + pAttr ) ); + + // actual area for the shape (without subsetting, but + // including char scaling) + const ::basegfx::B2DRectangle& rShapeBoundsPixel( + calcUpdateAreaPixel( ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0), + aShapeTransformation, + rCanvasTransform, + pAttr ) ); + + // nominal area for the shape (without subsetting, without + // char scaling). NOTE: to cancel the shape translation, + // contained in rSpriteBoundsPixel, this is _without_ any + // translational component (fixed along with #121921#). + ::basegfx::B2DRectangle aLogShapeBounds; + const ::basegfx::B2DRectangle& rNominalShapeBoundsPixel( + shapeArea2AreaPixel( rCanvasTransform, + ::canvas::tools::calcTransformedRectBounds( + aLogShapeBounds, + ::basegfx::B2DRectangle(0.0,0.0,1.0,1.0), + aNonTranslationalShapeTransformation ) ) ); + + // create (or resize) sprite with sprite's pixel size, if + // not done already + const ::basegfx::B2DSize& rSpriteSizePixel(rSpriteBoundsPixel.getRange()); + if( !mpSprite ) + { + mpSprite.reset( + new AnimatedSprite( mpViewLayer, + rSpriteSizePixel, + nPrio )); + } + else + { + // TODO(F2): when the sprite _actually_ gets resized, + // content needs a repaint! + mpSprite->resize( rSpriteSizePixel ); + } + + ENSURE_OR_RETURN_FALSE( mpSprite, "ViewShape::renderSprite(): No sprite" ); + + VERBOSE_TRACE( "ViewShape::renderSprite(): Rendering sprite 0x%X", + mpSprite.get() ); + + + // always show the sprite (might have been hidden before) + mpSprite->show(); + + // determine center of sprite output position in pixel + // (assumption here: all shape transformations have the + // shape center as the pivot point). From that, subtract + // distance of rSpriteBoundsPixel's left, top edge from + // rShapeBoundsPixel's center. This moves the sprite at + // the appropriate output position within the virtual + // rShapeBoundsPixel area. + ::basegfx::B2DPoint aSpritePosPixel( rBounds.getCenter() ); + aSpritePosPixel *= rCanvasTransform; + aSpritePosPixel -= rShapeBoundsPixel.getCenter() - rSpriteBoundsPixel.getMinimum(); + + // the difference between rShapeBoundsPixel and + // rSpriteBoundsPixel upper, left corner is: the offset we + // have to move sprite output to the right, top (to make + // the desired subset content visible at all) + const ::basegfx::B2DSize& rSpriteCorrectionOffset( + rSpriteBoundsPixel.getMinimum() - rNominalShapeBoundsPixel.getMinimum() ); + + // offset added top, left for anti-aliasing (otherwise, + // shapes fully filling the sprite will have anti-aliased + // pixel cut off) + const ::basegfx::B2DSize aAAOffset( + ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE, + ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE ); + + // set pixel output offset to sprite: we always leave + // ANTIALIASING_EXTRA_SIZE room atop and to the left, and, + // what's more, for subsetted shapes, we _have_ to cancel + // the effect of the shape renderer outputting the subset + // at its absolute position inside the shape, instead of + // at the origin. + // NOTE: As for now, sprites are always positioned on + // integer pixel positions on screen, have to round to + // nearest integer here, too (fixed along with #121921#) + mpSprite->setPixelOffset( + aAAOffset - ::basegfx::B2DSize( + ::basegfx::fround( rSpriteCorrectionOffset.getX() ), + ::basegfx::fround( rSpriteCorrectionOffset.getY() ) ) ); + + // always set sprite position and transformation, since + // they do not relate directly to the update flags + // (e.g. sprite position changes when sprite size changes) + mpSprite->movePixel( aSpritePosPixel ); + mpSprite->transform( getSpriteTransformation( rSpriteSizePixel, + rOrigBounds.getRange(), + pAttr ) ); + + + // process flags + // ============= + + bool bRedrawRequired( mbForceUpdate || (nUpdateFlags & FORCE) ); + + if( mbForceUpdate || (nUpdateFlags & ALPHA) ) + { + mpSprite->setAlpha( (pAttr && pAttr->isAlphaValid()) ? + ::basegfx::clamp(pAttr->getAlpha(), + 0.0, + 1.0) : + 1.0 ); + } + if( mbForceUpdate || (nUpdateFlags & CLIP) ) + { + if( pAttr && pAttr->isClipValid() ) + { + ::basegfx::B2DPolyPolygon aClipPoly( pAttr->getClip() ); + + // extract linear part of canvas view transformation + // (linear means: without translational components) + ::basegfx::B2DHomMatrix aViewTransform( + mpViewLayer->getTransformation() ); + aViewTransform.set( 0, 2, 0.0 ); + aViewTransform.set( 1, 2, 0.0 ); + + // make the clip 2*ANTIALIASING_EXTRA_SIZE larger + // such that it's again centered over the sprite. + aViewTransform.scale(rSpriteSizePixel.getX()/ + (rSpriteSizePixel.getX()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE), + rSpriteSizePixel.getY()/ + (rSpriteSizePixel.getY()-2*::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE)); + + // transform clip polygon from view to device + // coordinate space + aClipPoly.transform( aViewTransform ); + + mpSprite->clip( aClipPoly ); + } + else + mpSprite->clip(); + } + if( mbForceUpdate || (nUpdateFlags & CONTENT) ) + { + bRedrawRequired = true; + + // TODO(P1): maybe provide some appearance change methods at + // the Renderer interface + + // force the renderer to be regenerated below, for the + // different attributes to take effect + invalidateRenderer(); + } + + mbForceUpdate = false; + + if( !bRedrawRequired ) + return true; + + + // sprite needs repaint - output to sprite canvas + // ============================================== + + ::cppcanvas::CanvasSharedPtr pContentCanvas( mpSprite->getContentCanvas() ); + + return draw( pContentCanvas, + rMtf, + pAttr, + aShapeTransformation, + NULL, // clipping is done via Sprite::clip() + rSubsets ); + } + + bool ViewShape::render( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ::basegfx::B2DRectangle& rBounds, + const ::basegfx::B2DRectangle& rUpdateBounds, + int nUpdateFlags, + const ShapeAttributeLayerSharedPtr& pAttr, + const VectorOfDocTreeNodes& rSubsets, + bool bIsVisible ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::render()" ); + + // TODO(P1): For multiple views, it might pay off to reorg Shape and ViewShape, + // in that all the common setup steps here are refactored to Shape (would then + // have to be performed only _once_ per Shape paint). + + if( !bIsVisible ) + { + VERBOSE_TRACE( "ViewShape::render(): skipping shape %X", this ); + + // shape is invisible, no need to update anything. + return true; + } + + // since we have no sprite here, _any_ update request + // translates into a required redraw. + bool bRedrawRequired( mbForceUpdate || nUpdateFlags != 0 ); + + if( (nUpdateFlags & CONTENT) ) + { + // TODO(P1): maybe provide some appearance change methods at + // the Renderer interface + + // force the renderer to be regenerated below, for the + // different attributes to take effect + invalidateRenderer(); + } + + mbForceUpdate = false; + + if( !bRedrawRequired ) + return true; + + VERBOSE_TRACE( "ViewShape::render(): rendering shape %X at position (%f,%f)", + this, + rBounds.getMinX(), + rBounds.getMinY() ); + + + // shape needs repaint - setup all that's needed + // --------------------------------------------- + + boost::optional<basegfx::B2DPolyPolygon> aClip; + + if( pAttr ) + { + // setup clip poly + if( pAttr->isClipValid() ) + aClip.reset( pAttr->getClip() ); + + // emulate global shape alpha by first rendering into + // a temp bitmap, and then to screen (this would have + // been much easier if we'd be currently a sprite - + // see above) + if( pAttr->isAlphaValid() ) + { + const double nAlpha( pAttr->getAlpha() ); + + if( !::basegfx::fTools::equalZero( nAlpha ) && + !::rtl::math::approxEqual(nAlpha, 1.0) ) + { + // render with global alpha - have to prepare + // a bitmap, and render that with modulated + // alpha + // ------------------------------------------- + + const ::basegfx::B2DHomMatrix aTransform( + getShapeTransformation( rBounds, + pAttr ) ); + + // TODO(P1): Should the view transform some + // day contain rotation/shear, transforming + // the original bounds with the total + // transformation might result in smaller + // overall bounds. + + // determine output rect of _shape update + // area_ in device pixel + const ::basegfx::B2DHomMatrix aCanvasTransform( + rDestinationCanvas->getTransformation() ); + ::basegfx::B2DRectangle aTmpRect; + ::canvas::tools::calcTransformedRectBounds( aTmpRect, + rUpdateBounds, + aCanvasTransform ); + + // pixel size of cache bitmap: round up to + // nearest int + const ::basegfx::B2ISize aBmpSize( static_cast<sal_Int32>( aTmpRect.getWidth() )+1, + static_cast<sal_Int32>( aTmpRect.getHeight() )+1 ); + + // try to fetch temporary surface for alpha + // compositing (to achieve the global alpha + // blend effect, have to first render shape as + // a whole, then blit that surface with global + // alpha to the destination) + const RendererCacheVector::iterator aCompositingSurface( + getCacheEntry( rDestinationCanvas ) ); + + if( !aCompositingSurface->mpLastBitmapCanvas || + aCompositingSurface->mpLastBitmapCanvas->getSize() != aBmpSize ) + { + // create a bitmap of appropriate size + ::cppcanvas::BitmapSharedPtr pBitmap( + ::cppcanvas::BaseGfxFactory::getInstance().createAlphaBitmap( + rDestinationCanvas, + aBmpSize ) ); + + ENSURE_OR_THROW(pBitmap, + "ViewShape::render(): Could not create compositing surface"); + + aCompositingSurface->mpDestinationCanvas = rDestinationCanvas; + aCompositingSurface->mpLastBitmap = pBitmap; + aCompositingSurface->mpLastBitmapCanvas = pBitmap->getBitmapCanvas(); + } + + // buffer aCompositingSurface iterator content + // - said one might get invalidated during + // draw() below. + ::cppcanvas::BitmapCanvasSharedPtr pBitmapCanvas( + aCompositingSurface->mpLastBitmapCanvas ); + + ::cppcanvas::BitmapSharedPtr pBitmap( + aCompositingSurface->mpLastBitmap); + + // setup bitmap canvas transformation - + // which happens to be the destination + // canvas transformation without any + // translational components. + // + // But then, the render transformation as + // calculated by getShapeTransformation() + // above outputs the shape at its real + // destination position. Thus, we have to + // offset the output back to the origin, + // for which we simply plug in the + // negative position of the left, top edge + // of the shape's bound rect in device + // pixel into aLinearTransform below. + ::basegfx::B2DHomMatrix aAdjustedCanvasTransform( aCanvasTransform ); + aAdjustedCanvasTransform.translate( -aTmpRect.getMinX(), + -aTmpRect.getMinY() ); + + pBitmapCanvas->setTransformation( aAdjustedCanvasTransform ); + + // TODO(P2): If no update flags, or only + // alpha_update is set, we can save us the + // rendering into the bitmap (uh, it's not + // _that_ easy - for a forced redraw, + // e.g. when ending an animation, we always + // get UPDATE_FORCE here). + + // render into this bitmap + if( !draw( pBitmapCanvas, + rMtf, + pAttr, + aTransform, + !aClip ? NULL : &(*aClip), + rSubsets ) ) + { + return false; + } + + // render bitmap to screen, with given global + // alpha. Since the bitmap already contains + // pixel-equivalent output, we have to use the + // inverse view transformation, adjusted with + // the final shape output position (note: + // cannot simply change the view + // transformation here, as that would affect a + // possibly set clip!) + ::basegfx::B2DHomMatrix aBitmapTransform( aCanvasTransform ); + OSL_ENSURE( aBitmapTransform.isInvertible(), + "ViewShape::render(): View transformation is singular!" ); + + aBitmapTransform.invert(); + + const basegfx::B2DHomMatrix aTranslation(basegfx::tools::createTranslateB2DHomMatrix( + aTmpRect.getMinX(), aTmpRect.getMinY())); + + aBitmapTransform = aBitmapTransform * aTranslation; + pBitmap->setTransformation( aBitmapTransform ); + + // finally, render bitmap alpha-modulated + pBitmap->drawAlphaModulated( nAlpha ); + + return true; + } + } + } + + // retrieve shape transformation, _with_ shape translation + // to actual page position. + const ::basegfx::B2DHomMatrix aTransform( + getShapeTransformation( rBounds, + pAttr ) ); + + return draw( rDestinationCanvas, + rMtf, + pAttr, + aTransform, + !aClip ? NULL : &(*aClip), + rSubsets ); + } + + + // ------------------------------------------------------------------------------------- + + ViewShape::ViewShape( const ViewLayerSharedPtr& rViewLayer ) : + mpViewLayer( rViewLayer ), + maRenderers(), + mpSprite(), + mbAnimationMode( false ), + mbForceUpdate( true ) + { + ENSURE_OR_THROW( mpViewLayer, "ViewShape::ViewShape(): Invalid View" ); + } + + ViewLayerSharedPtr ViewShape::getViewLayer() const + { + return mpViewLayer; + } + + ViewShape::RendererCacheVector::iterator ViewShape::getCacheEntry( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas ) const + { + // lookup destination canvas - is there already a renderer + // created for that target? + RendererCacheVector::iterator aIter; + const RendererCacheVector::iterator aEnd( maRenderers.end() ); + + // already there? + if( (aIter=::std::find_if( maRenderers.begin(), + aEnd, + ::boost::bind( + ::std::equal_to< ::cppcanvas::CanvasSharedPtr >(), + ::boost::cref( rDestinationCanvas ), + ::boost::bind( + &RendererCacheEntry::getDestinationCanvas, + _1 ) ) ) ) == aEnd ) + { + if( maRenderers.size() >= MAX_RENDER_CACHE_ENTRIES ) + { + // cache size exceeded - prune entries. For now, + // simply remove the first one, which of course + // breaks for more complex access schemes. But in + // general, this leads to most recently used + // entries to reside at the end of the vector. + maRenderers.erase( maRenderers.begin() ); + + // ATTENTION: after this, both aIter and aEnd are + // invalid! + } + + // not yet in cache - add default-constructed cache + // entry, to have something to return + maRenderers.push_back( RendererCacheEntry() ); + aIter = maRenderers.end()-1; + } + + return aIter; + } + + ::cppcanvas::RendererSharedPtr ViewShape::getRenderer( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ShapeAttributeLayerSharedPtr& rAttr ) const + { + // lookup destination canvas - is there already a renderer + // created for that target? + const RendererCacheVector::iterator aIter( + getCacheEntry( rDestinationCanvas ) ); + + // now we have a valid entry, either way. call prefetch() + // on it, nevertheless - maybe the metafile changed, and + // the renderer still needs an update (prefetch() will + // detect that) + if( prefetch( *aIter, + rDestinationCanvas, + rMtf, + rAttr ) ) + { + return aIter->mpRenderer; + } + else + { + // prefetch failed - renderer is invalid + return ::cppcanvas::RendererSharedPtr(); + } + } + + void ViewShape::invalidateRenderer() const + { + // simply clear the cache. Subsequent getRenderer() calls + // will regenerate the Renderers. + maRenderers.clear(); + } + + ::basegfx::B2DSize ViewShape::getAntialiasingBorder() const + { + ENSURE_OR_THROW( mpViewLayer->getCanvas(), + "ViewShape::getAntialiasingBorder(): Invalid ViewLayer canvas" ); + + const ::basegfx::B2DHomMatrix& rViewTransform( + mpViewLayer->getTransformation() ); + + // TODO(F1): As a quick shortcut (did not want to invert + // whole matrix here), taking only scale components of + // view transformation matrix. This will be wrong when + // e.g. shearing is involved. + const double nXBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(0,0) ); + const double nYBorder( ::cppcanvas::Canvas::ANTIALIASING_EXTRA_SIZE / rViewTransform.get(1,1) ); + + return ::basegfx::B2DSize( nXBorder, + nYBorder ); + } + + bool ViewShape::enterAnimationMode() + { + mbForceUpdate = true; + mbAnimationMode = true; + + return true; + } + + void ViewShape::leaveAnimationMode() + { + mpSprite.reset(); + mbAnimationMode = false; + mbForceUpdate = true; + } + + bool ViewShape::update( const GDIMetaFileSharedPtr& rMtf, + const RenderArgs& rArgs, + int nUpdateFlags, + bool bIsVisible ) const + { + RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::ViewShape::update()" ); + ENSURE_OR_RETURN_FALSE( mpViewLayer->getCanvas(), "ViewShape::update(): Invalid layer canvas" ); + + // Shall we render to a sprite, or to a plain canvas? + if( isBackgroundDetached() ) + return renderSprite( mpViewLayer, + rMtf, + rArgs.maOrigBounds, + rArgs.maBounds, + rArgs.maUnitBounds, + nUpdateFlags, + rArgs.mrAttr, + rArgs.mrSubsets, + rArgs.mnShapePriority, + bIsVisible ); + else + return render( mpViewLayer->getCanvas(), + rMtf, + rArgs.maBounds, + rArgs.maUpdateBounds, + nUpdateFlags, + rArgs.mrAttr, + rArgs.mrSubsets, + bIsVisible ); + } + + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/slideshow/source/engine/shapes/viewshape.hxx b/slideshow/source/engine/shapes/viewshape.hxx new file mode 100644 index 000000000000..2ab97a27ca98 --- /dev/null +++ b/slideshow/source/engine/shapes/viewshape.hxx @@ -0,0 +1,343 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_VIEWSHAPE_HXX +#define INCLUDED_SLIDESHOW_VIEWSHAPE_HXX + +#include <cppcanvas/renderer.hxx> +#include <cppcanvas/bitmap.hxx> + +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +#include "tools.hxx" +#include "shapeattributelayer.hxx" +#include "animatedsprite.hxx" +#include "viewlayer.hxx" +#include "doctreenode.hxx" + +#include <vector> + + +namespace slideshow +{ + namespace internal + { + /** This class is the viewable representation of a draw + document's XShape, associated to a specific View + + The class is able to render the associated XShape on + View implementations. + */ + class ViewShape : private boost::noncopyable + { + public: + /** Create a ViewShape for the given View + + @param rView + The associated View object. + */ + explicit ViewShape( const ViewLayerSharedPtr& rViewLayer ); + + /** Query the associated view layer of this shape + */ + ViewLayerSharedPtr getViewLayer() const; + + /** Query dimension of a safety border around the shape for AA + + If the view performs antialiasing, this method + calculates a safety border around the shape, in the + shape coordinate system, which is guaranteed to + include every pixel touched when rendering the shape. + */ + ::basegfx::B2DSize getAntialiasingBorder() const; + + + // animation methods + //------------------------------------------------------------------ + + /** Notify the ViewShape that an animation starts now + + This method enters animation mode on the associate + target view. The shape can be animated in parallel on + different views. + + @return whether the mode change finished successfully. + */ + bool enterAnimationMode(); + + /** Notify the ViewShape that it is no longer animated + + This methods ends animation mode on the associate + target view + */ + void leaveAnimationMode(); + + /** Query whether the ViewShape is currently animated + + This method checks whether the ViewShape is currently in + animation mode. + */ + bool isBackgroundDetached() const { return mbAnimationMode; } + + + // render methods + //------------------------------------------------------------------ + + enum UpdateFlags + { + NONE= 0, + TRANSFORMATION= 1, + CLIP= 2, + ALPHA= 4, + POSITION= 8, + CONTENT= 16, + FORCE= 32 + }; + + struct RenderArgs + { + /** Create render argument struct + + @param rOrigBounds + The initial shape bounds + + @param rUpdateBounds + The area covered by the shape + + @param rBounds + The current shape bounds + + @param rAttr + The current shape attribute set. Can be NULL, for + default attributes. Attention: stored as a reference, + thus, parameter object must stay valid! + + @param rSubsets + Vector of subset rendering ranges. Attention: + stored as a reference, thus, parameter object must + stay valid! + + @param nPrio + Shape priority + */ + RenderArgs( const ::basegfx::B2DRectangle& rOrigBounds, + const ::basegfx::B2DRectangle& rUpdateBounds, + const ::basegfx::B2DRectangle& rBounds, + const ::basegfx::B2DRectangle& rUnitBounds, + const ShapeAttributeLayerSharedPtr& rAttr, + const VectorOfDocTreeNodes& rSubsets, + double nPrio ) : + maOrigBounds( rOrigBounds ), + maUpdateBounds( rUpdateBounds ), + maBounds( rBounds ), + maUnitBounds( rUnitBounds ), + mrAttr( rAttr ), + mrSubsets( rSubsets ), + mnShapePriority( nPrio ) + { + } + + const ::basegfx::B2DRectangle maOrigBounds; + const ::basegfx::B2DRectangle maUpdateBounds; + const ::basegfx::B2DRectangle maBounds; + const ::basegfx::B2DRectangle maUnitBounds; + const ShapeAttributeLayerSharedPtr& mrAttr; + const VectorOfDocTreeNodes& mrSubsets; + const double mnShapePriority; + }; + + /** Update the ViewShape + + This method updates the ViewShape on the associated + view. If the shape is currently animated, the render + target is the sprite, otherwise the view's + canvas. This method does not render anything, if the + update flags are 0. + + @param rMtf + The metafile representation of the shape + + @param rArgs + Parameter structure, containing all necessary arguments + + @param nUpdateFlags + Bitmask of things to update. Use FORCE to force a repaint. + + @param bIsVisible + When false, the shape is fully invisible (and possibly + don't need to be painted) + + @return whether the rendering finished successfully. + */ + bool update( const GDIMetaFileSharedPtr& rMtf, + const RenderArgs& rArgs, + int nUpdateFlags, + bool bIsVisible ) const; + + /** Retrieve renderer for given canvas and metafile. + + If necessary, the renderer is created or updated for + the metafile and attribute layer. + + @return a renderer that renders to the given + destination canvas + */ + ::cppcanvas::RendererSharedPtr getRenderer( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ShapeAttributeLayerSharedPtr& rAttr ) const; + + + private: + struct RendererCacheEntry + { + RendererCacheEntry() : + mpDestinationCanvas(), + mpRenderer(), + mpMtf(), + mpLastBitmap(), + mpLastBitmapCanvas() + { + } + + ::cppcanvas::CanvasSharedPtr getDestinationCanvas() + { + return mpDestinationCanvas; + } + + ::cppcanvas::CanvasSharedPtr mpDestinationCanvas; + ::cppcanvas::RendererSharedPtr mpRenderer; + GDIMetaFileSharedPtr mpMtf; + ::cppcanvas::BitmapSharedPtr mpLastBitmap; + ::cppcanvas::BitmapCanvasSharedPtr mpLastBitmapCanvas; + }; + + typedef ::std::vector< RendererCacheEntry > RendererCacheVector; + + + /** Prefetch Renderer for given canvas + */ + bool prefetch( RendererCacheEntry& io_rCacheEntry, + const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ShapeAttributeLayerSharedPtr& rAttr ) const; + + /** Draw with prefetched Renderer to stored canvas + + This method draws prefetched Renderer to its + associated canvas (which happens to be mpLastCanvas). + */ + bool draw( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ShapeAttributeLayerSharedPtr& rAttr, + const ::basegfx::B2DHomMatrix& rTransform, + const ::basegfx::B2DPolyPolygon* pClip, + const VectorOfDocTreeNodes& rSubsets ) const; + + /** Render shape to an active sprite + */ + bool renderSprite( const ViewLayerSharedPtr& rViewLayer, + const GDIMetaFileSharedPtr& rMtf, + const ::basegfx::B2DRectangle& rOrigBounds, + const ::basegfx::B2DRectangle& rBounds, + const ::basegfx::B2DRectangle& rUnitBounds, + int nUpdateFlags, + const ShapeAttributeLayerSharedPtr& pAttr, + const VectorOfDocTreeNodes& rSubsets, + double nPrio, + bool bIsVisible ) const; + + /** Render shape to given canvas + */ + bool render( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas, + const GDIMetaFileSharedPtr& rMtf, + const ::basegfx::B2DRectangle& rBounds, + const ::basegfx::B2DRectangle& rUpdateBounds, + int nUpdateFlags, + const ShapeAttributeLayerSharedPtr& pAttr, + const VectorOfDocTreeNodes& rSubsets, + bool bIsVisible ) const; + + /** Calc sprite size in pixel + + Converts user coordinate system to device pixel, and + adds antialiasing border. + + @param rUserSize + Size of the sprite in user coordinate system (doc coordinates) + */ + ::basegfx::B2DSize calcSpriteSizePixel( const ::basegfx::B2DSize& rUserSize ) const; + + enum{ MAX_RENDER_CACHE_ENTRIES=2 }; + + /** Retrieve a valid iterator to renderer cache entry + + This method ensures that an internal limit of + MAX_RENDER_CACHE_ENTRIES is not exceeded. + + @param rDestinationCanvas + Destination canvas to retrieve cache entry for + + @return a valid iterator to a renderer cache entry for + the given canvas. The entry might be + default-constructed (if newly added) + */ + RendererCacheVector::iterator getCacheEntry( const ::cppcanvas::CanvasSharedPtr& rDestinationCanvas ) const; + + void invalidateRenderer() const; + + /** The view layer this object is part of. + + Needed for sprite creation + */ + ViewLayerSharedPtr mpViewLayer; + + /// A set of cached mtf/canvas combinations + mutable RendererCacheVector maRenderers; + + /// The sprite object + mutable AnimatedSpriteSharedPtr mpSprite; + + /// If true, render() calls go to the sprite + mutable bool mbAnimationMode; + + /// If true, shape needs full repaint (and the sprite a setup, if any) + mutable bool mbForceUpdate; + }; + + typedef ::boost::shared_ptr< ViewShape > ViewShapeSharedPtr; + + } +} + +#endif /* INCLUDED_SLIDESHOW_VIEWSHAPE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |