diff options
Diffstat (limited to 'sc/source/ui/vba/vbasheetobjects.cxx')
-rw-r--r-- | sc/source/ui/vba/vbasheetobjects.cxx | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/sc/source/ui/vba/vbasheetobjects.cxx b/sc/source/ui/vba/vbasheetobjects.cxx new file mode 100644 index 000000000..e0082be6b --- /dev/null +++ b/sc/source/ui/vba/vbasheetobjects.cxx @@ -0,0 +1,555 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "vbasheetobjects.hxx" +#include <vector> +#include <rtl/math.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/form/XFormComponent.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include "vbasheetobject.hxx" +#include <cppuhelper/implbase.hxx> + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +namespace { + +template< typename Type > +bool lclGetProperty( Type& orValue, const uno::Reference< beans::XPropertySet >& rxPropSet, const OUString& rPropName ) +{ + try + { + return rxPropSet->getPropertyValue( rPropName ) >>= orValue; + } + catch( uno::Exception& ) + { + } + return false; +} + +/** Rounds the passed value to a multiple of 0.75 and converts it to 1/100 mm. + + @throws uno::RuntimeException +*/ +double lclPointsToHmm( const uno::Any& rPoints ) +{ + return PointsToHmm( ::rtl::math::approxFloor( rPoints.get< double >() / 0.75 ) * 0.75 ); +} + +} // namespace + +// Base implementations + +/** Container for a specific type of drawing object in a spreadsheet. + + Derived classes provide all required functionality specific to the type of + shapes covered by the container. + */ +class ScVbaObjectContainer : public ::cppu::WeakImplHelper< container::XIndexAccess > +{ +public: + /// @throws uno::RuntimeException + explicit ScVbaObjectContainer( + const uno::Reference< XHelperInterface >& rxParent, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XModel >& rxModel, + const uno::Reference< sheet::XSpreadsheet >& rxSheet, + const uno::Type& rVbaType ); + + /** Returns the VBA helper interface of the VBA collection object. */ + const uno::Reference< XHelperInterface >& getParent() const { return mxParent; } + /** Returns the component context of the VBA collection object. */ + const uno::Reference< uno::XComponentContext >& getContext() const { return mxContext; } + /** Returns the VBA type information of the objects in this container. */ + const uno::Type& getVbaType() const { return maVbaType; } + + /** Collects all shapes supported by this instance and inserts them into + the internal shape vector. + + @throws uno::RuntimeException + */ + void collectShapes(); + /** Creates and returns a new UNO shape. + + @throws uno::RuntimeException + */ + uno::Reference< drawing::XShape > createShape( const awt::Point& rPos, const awt::Size& rSize ); + /** Inserts the passed shape into the draw page and into this container, and returns its index in the draw page. + + @throws uno::RuntimeException + */ + sal_Int32 insertShape( const uno::Reference< drawing::XShape >& rxShape ); + /** Creates and returns a new VBA implementation object for the passed shape. + + @throws uno::RuntimeException + */ + ::rtl::Reference< ScVbaSheetObjectBase > createVbaObject( const uno::Reference< drawing::XShape >& rxShape ); + /** Creates and returns a new VBA implementation object for the passed shape in an Any. + + @throws uno::RuntimeException + */ + uno::Any createCollectionObject( const uno::Any& rSource ); + /** Returns the VBA implementation object with the specified name. + + @throws uno::RuntimeException + */ + uno::Any getItemByStringIndex( const OUString& rIndex ); + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override; + virtual uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) override; + + // XElementAccess + virtual uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + +protected: + /** Derived classes return true, if the passed shape is supported by the instance. */ + virtual bool implPickShape( const uno::Reference< drawing::XShape >& rxShape ) const = 0; + /** Derived classes create and return a new VBA implementation object for the passed shape. + + @throws uno::RuntimeException + */ + virtual ScVbaSheetObjectBase* implCreateVbaObject( const uno::Reference< drawing::XShape >& rxShape ) = 0; + /** Derived classes return the service name of the UNO shape. */ + virtual OUString implGetShapeServiceName() const = 0; + + /** Returns the shape name via 'Name' property of the UNO shape. May be overwritten. + + @throws uno::RuntimeException + */ + virtual OUString implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const; + /** Is called when a new UNO shape has been created but not yet inserted into the drawing page. + + @throws uno::RuntimeException + */ + virtual void implOnShapeCreated( const uno::Reference< drawing::XShape >& rxShape ); + +protected: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< frame::XModel > mxModel; + uno::Reference< lang::XMultiServiceFactory > mxFactory; + uno::Reference< drawing::XShapes > mxShapes; + +private: + typedef ::std::vector< uno::Reference< drawing::XShape > > ShapeVector; + const uno::Type maVbaType; + ShapeVector maShapes; +}; + +ScVbaObjectContainer::ScVbaObjectContainer( + const uno::Reference< XHelperInterface >& rxParent, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XModel >& rxModel, + const uno::Reference< sheet::XSpreadsheet >& rxSheet, + const uno::Type& rVbaType ) : + mxParent( rxParent ), + mxContext( rxContext ), + mxModel( rxModel, uno::UNO_SET_THROW ), + mxFactory( rxModel, uno::UNO_QUERY_THROW ), + maVbaType( rVbaType ) +{ + uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupp( rxSheet, uno::UNO_QUERY_THROW ); + mxShapes.set( xDrawPageSupp->getDrawPage(), uno::UNO_QUERY_THROW ); +} + +void ScVbaObjectContainer::collectShapes() +{ + maShapes.clear(); + for( sal_Int32 nIndex = 0, nCount = mxShapes->getCount(); nIndex < nCount; ++nIndex ) + { + uno::Reference< drawing::XShape > xShape( mxShapes->getByIndex( nIndex ), uno::UNO_QUERY_THROW ); + if( implPickShape( xShape ) ) + maShapes.push_back( xShape ); + } +} + +uno::Reference< drawing::XShape > ScVbaObjectContainer::createShape( const awt::Point& rPos, const awt::Size& rSize ) +{ + uno::Reference< drawing::XShape > xShape( mxFactory->createInstance( implGetShapeServiceName() ), uno::UNO_QUERY_THROW ); + xShape->setPosition( rPos ); + xShape->setSize( rSize ); + implOnShapeCreated( xShape ); + return xShape; +} + +sal_Int32 ScVbaObjectContainer::insertShape( const uno::Reference< drawing::XShape >& rxShape ) +{ + mxShapes->add( rxShape ); + maShapes.push_back( rxShape ); + return mxShapes->getCount() - 1; +} + +::rtl::Reference< ScVbaSheetObjectBase > ScVbaObjectContainer::createVbaObject( + const uno::Reference< drawing::XShape >& rxShape ) +{ + return implCreateVbaObject( rxShape ); +} + +uno::Any ScVbaObjectContainer::createCollectionObject( const uno::Any& rSource ) +{ + uno::Reference< drawing::XShape > xShape( rSource, uno::UNO_QUERY_THROW ); + uno::Reference< excel::XSheetObject > xSheetObject( implCreateVbaObject( xShape ) ); + return uno::Any( xSheetObject ); +} + +uno::Any ScVbaObjectContainer::getItemByStringIndex( const OUString& rIndex ) +{ + auto aIt = std::find_if(maShapes.begin(), maShapes.end(), + [&rIndex, this](const ShapeVector::value_type& rxShape) { return rIndex == implGetShapeName( rxShape ); }); + if (aIt != maShapes.end()) + return createCollectionObject( uno::Any( *aIt ) ); + throw uno::RuntimeException(); +} + +// XIndexAccess + +sal_Int32 SAL_CALL ScVbaObjectContainer::getCount() +{ + return static_cast< sal_Int32 >( maShapes.size() ); +} + +uno::Any SAL_CALL ScVbaObjectContainer::getByIndex( sal_Int32 nIndex ) +{ + if( (0 <= nIndex) && (nIndex < getCount()) ) + return uno::Any( maShapes[ static_cast< size_t >( nIndex ) ] ); + throw lang::IndexOutOfBoundsException(); +} + +// XElementAccess + +uno::Type SAL_CALL ScVbaObjectContainer::getElementType() +{ + return cppu::UnoType<drawing::XShape>::get(); +} + +sal_Bool SAL_CALL ScVbaObjectContainer::hasElements() +{ + return !maShapes.empty(); +} + +// private + +OUString ScVbaObjectContainer::implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const +{ + uno::Reference< beans::XPropertySet > xPropSet( rxShape, uno::UNO_QUERY_THROW ); + return xPropSet->getPropertyValue( "Name" ).get< OUString >(); +} + +void ScVbaObjectContainer::implOnShapeCreated( const uno::Reference< drawing::XShape >& /*rxShape*/ ) +{ +} + +namespace { + +class ScVbaObjectEnumeration : public SimpleEnumerationBase +{ +public: + explicit ScVbaObjectEnumeration( const ScVbaObjectContainerRef& rxContainer ); + virtual uno::Any createCollectionObject( const uno::Any& rSource ) override; + +private: + ScVbaObjectContainerRef mxContainer; +}; + +} + +ScVbaObjectEnumeration::ScVbaObjectEnumeration( const ScVbaObjectContainerRef& rxContainer ) : + SimpleEnumerationBase( rxContainer.get() ), + mxContainer( rxContainer ) +{ +} + +uno::Any ScVbaObjectEnumeration::createCollectionObject( const uno::Any& rSource ) +{ + return mxContainer->createCollectionObject( rSource ); +} + +ScVbaSheetObjectsBase::ScVbaSheetObjectsBase( const ScVbaObjectContainerRef& rxContainer ) : + ScVbaSheetObjects_BASE( rxContainer->getParent(), rxContainer->getContext(), rxContainer.get() ), + mxContainer( rxContainer ) +{ + mxContainer->collectShapes(); +} + +ScVbaSheetObjectsBase::~ScVbaSheetObjectsBase() +{ +} + +void ScVbaSheetObjectsBase::collectShapes() +{ + mxContainer->collectShapes(); +} + +// XEnumerationAccess + +uno::Reference< container::XEnumeration > SAL_CALL ScVbaSheetObjectsBase::createEnumeration() +{ + return new ScVbaObjectEnumeration( mxContainer ); +} + +// XElementAccess + +uno::Type SAL_CALL ScVbaSheetObjectsBase::getElementType() +{ + return mxContainer->getVbaType(); +} + +// ScVbaCollectionBase + +uno::Any ScVbaSheetObjectsBase::createCollectionObject( const uno::Any& rSource ) +{ + return mxContainer->createCollectionObject( rSource ); +} + +uno::Any ScVbaSheetObjectsBase::getItemByStringIndex( const OUString& rIndex ) +{ + return mxContainer->getItemByStringIndex( rIndex ); +} + +// Graphic object containers supporting ooo.vba.excel.XGraphicObject + +ScVbaGraphicObjectsBase::ScVbaGraphicObjectsBase( const ScVbaObjectContainerRef& rxContainer ) : + ScVbaGraphicObjects_BASE( rxContainer ) +{ +} + +// XGraphicObjects + +uno::Any SAL_CALL ScVbaGraphicObjectsBase::Add( const uno::Any& rLeft, const uno::Any& rTop, const uno::Any& rWidth, const uno::Any& rHeight ) +{ + /* Extract double values from passed Anys (the lclPointsToHmm() helper + function will throw a RuntimeException on any error), and convert from + points to 1/100 mm. */ + awt::Point aPos( static_cast<sal_Int32>(lclPointsToHmm( rLeft )), static_cast<sal_Int32>(lclPointsToHmm( rTop )) ); + awt::Size aSize( static_cast<sal_Int32>(lclPointsToHmm( rWidth )), static_cast<sal_Int32>(lclPointsToHmm( rHeight )) ); + // TODO: translate coordinates for RTL sheets + if( (aPos.X < 0) || (aPos.Y < 0) || (aSize.Width <= 0) || (aSize.Height <= 0) ) + throw uno::RuntimeException(); + + // create the UNO shape + uno::Reference< drawing::XShape > xShape( mxContainer->createShape( aPos, aSize ), uno::UNO_SET_THROW ); + sal_Int32 nIndex = mxContainer->insertShape( xShape ); + + // create and return the VBA object + ::rtl::Reference< ScVbaSheetObjectBase > xVbaObject = mxContainer->createVbaObject( xShape ); + xVbaObject->setDefaultProperties( nIndex ); + return uno::Any( uno::Reference< excel::XSheetObject >( xVbaObject.get() ) ); +} + +// Drawing controls + +namespace { + +class ScVbaControlContainer : public ScVbaObjectContainer +{ +public: + /// @throws uno::RuntimeException + explicit ScVbaControlContainer( + const uno::Reference< XHelperInterface >& rxParent, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XModel >& rxModel, + const uno::Reference< sheet::XSpreadsheet >& rxSheet, + const uno::Type& rVbaType, + const OUString& rModelServiceName, + sal_Int16 /* css::form::FormComponentType */ eType ); + +protected: + /// @throws uno::RuntimeException + uno::Reference< container::XIndexContainer > const & createForm(); + + virtual bool implPickShape( const uno::Reference< drawing::XShape >& rxShape ) const override; + virtual OUString implGetShapeServiceName() const override; + virtual bool implCheckProperties( const uno::Reference< beans::XPropertySet >& rxModelProps ) const; + virtual OUString implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const override; + virtual void implOnShapeCreated( const uno::Reference< drawing::XShape >& rxShape ) override; + +protected: + uno::Reference< container::XIndexContainer > mxFormIC; + OUString maModelServiceName; + sal_Int16 /* css::form::FormComponentType */ meType; +}; + +} + +ScVbaControlContainer::ScVbaControlContainer( + const uno::Reference< XHelperInterface >& rxParent, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XModel >& rxModel, + const uno::Reference< sheet::XSpreadsheet >& rxSheet, + const uno::Type& rVbaType, + const OUString& rModelServiceName, + sal_Int16 /* css::form::FormComponentType */ eType ) : + ScVbaObjectContainer( rxParent, rxContext, rxModel, rxSheet, rVbaType ), + maModelServiceName( rModelServiceName ), + meType( eType ) +{ +} + +uno::Reference< container::XIndexContainer > const & ScVbaControlContainer::createForm() +{ + if( !mxFormIC.is() ) + { + uno::Reference< form::XFormsSupplier > xFormsSupp( mxShapes, uno::UNO_QUERY_THROW ); + uno::Reference< container::XNameContainer > xFormsNC( xFormsSupp->getForms(), uno::UNO_SET_THROW ); + OUString aFormName = "Standard"; + if( xFormsNC->hasByName( aFormName ) ) + { + mxFormIC.set( xFormsNC->getByName( aFormName ), uno::UNO_QUERY_THROW ); + } + else + { + uno::Reference< form::XForm > xForm( mxFactory->createInstance( "com.sun.star.form.component.Form" ), uno::UNO_QUERY_THROW ); + xFormsNC->insertByName( aFormName, uno::Any( xForm ) ); + mxFormIC.set( xForm, uno::UNO_QUERY_THROW ); + } + } + return mxFormIC; +} + +bool ScVbaControlContainer::implPickShape( const uno::Reference< drawing::XShape >& rxShape ) const +{ + try + { + uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xModelProps( xControlShape->getControl(), uno::UNO_QUERY_THROW ); + sal_Int16 nClassId = -1; + return lclGetProperty( nClassId, xModelProps, "ClassId" ) && + (nClassId == meType) && implCheckProperties( xModelProps ); + } + catch( uno::Exception& ) + { + } + return false; +} + +OUString ScVbaControlContainer::implGetShapeServiceName() const +{ + return "com.sun.star.drawing.ControlShape"; +} + +bool ScVbaControlContainer::implCheckProperties( const uno::Reference< beans::XPropertySet >& /*rxModelProps*/ ) const +{ + return true; +} + +OUString ScVbaControlContainer::implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const +{ + uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW ); + return uno::Reference< container::XNamed >( xControlShape->getControl(), uno::UNO_QUERY_THROW )->getName(); +} + +void ScVbaControlContainer::implOnShapeCreated( const uno::Reference< drawing::XShape >& rxShape ) +{ + // passed shape must be a control shape + uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW ); + + // create the UNO control model + uno::Reference< form::XFormComponent > xFormComponent( mxFactory->createInstance( maModelServiceName ), uno::UNO_QUERY_THROW ); + uno::Reference< awt::XControlModel > xControlModel( xFormComponent, uno::UNO_QUERY_THROW ); + + // insert the control model into the form and the shape + createForm(); + mxFormIC->insertByIndex( mxFormIC->getCount(), uno::Any( xFormComponent ) ); + xControlShape->setControl( xControlModel ); +} + +// Push button + +namespace { + +class ScVbaButtonContainer : public ScVbaControlContainer +{ + bool mbOptionButtons; +public: + /// @throws uno::RuntimeException + explicit ScVbaButtonContainer( + const uno::Reference< XHelperInterface >& rxParent, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XModel >& rxModel, + const uno::Reference< sheet::XSpreadsheet >& rxSheet, + bool bOptionButtons); + +protected: + virtual ScVbaSheetObjectBase* implCreateVbaObject( const uno::Reference< drawing::XShape >& rxShape ) override; + virtual bool implCheckProperties( const uno::Reference< beans::XPropertySet >& rxModelProps ) const override; +}; + +} + +ScVbaButtonContainer::ScVbaButtonContainer( + const uno::Reference< XHelperInterface >& rxParent, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XModel >& rxModel, + const uno::Reference< sheet::XSpreadsheet >& rxSheet, + bool bOptionButtons ) : + ScVbaControlContainer( + rxParent, rxContext, rxModel, rxSheet, + cppu::UnoType<excel::XButton>::get(), + ( bOptionButtons ? + OUString( "com.sun.star.form.component.RadioButton" ) : + OUString( "com.sun.star.form.component.CommandButton" ) ), + ( bOptionButtons ? + form::FormComponentType::RADIOBUTTON : + form::FormComponentType::COMMANDBUTTON) ), + mbOptionButtons(bOptionButtons) +{ +} + +ScVbaSheetObjectBase* ScVbaButtonContainer::implCreateVbaObject( const uno::Reference< drawing::XShape >& rxShape ) +{ + uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW ); + return new ScVbaButton( mxParent, mxContext, mxModel, createForm(), xControlShape ); +} + +bool ScVbaButtonContainer::implCheckProperties( const uno::Reference< beans::XPropertySet >& rxModelProps ) const +{ + if (mbOptionButtons) + return true; + + // do not insert toggle buttons into the 'Buttons' collection + bool bToggle = false; + return lclGetProperty( bToggle, rxModelProps, "Toggle" ) && !bToggle; +} + +ScVbaButtons::ScVbaButtons( + const uno::Reference< XHelperInterface >& rxParent, + const uno::Reference< uno::XComponentContext >& rxContext, + const uno::Reference< frame::XModel >& rxModel, + const uno::Reference< sheet::XSpreadsheet >& rxSheet, + bool bOptionButtons) : + ScVbaGraphicObjectsBase( new ScVbaButtonContainer( rxParent, rxContext, rxModel, rxSheet, bOptionButtons ) ) +{ +} + +VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaButtons, "ooo.vba.excel.Buttons" ) + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |