diff options
Diffstat (limited to 'chart2/source/model/main/ChartModel.cxx')
-rw-r--r-- | chart2/source/model/main/ChartModel.cxx | 1301 |
1 files changed, 1301 insertions, 0 deletions
diff --git a/chart2/source/model/main/ChartModel.cxx b/chart2/source/model/main/ChartModel.cxx new file mode 100644 index 000000000..96c49fb56 --- /dev/null +++ b/chart2/source/model/main/ChartModel.cxx @@ -0,0 +1,1301 @@ +/* -*- 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 <ChartModel.hxx> +#include <servicenames.hxx> +#include <DataSourceHelper.hxx> +#include <ChartModelHelper.hxx> +#include <DisposeHelper.hxx> +#include <ControllerLockGuard.hxx> +#include <ObjectIdentifier.hxx> +#include "PageBackground.hxx" +#include <CloneHelper.hxx> +#include <NameContainer.hxx> +#include "UndoManager.hxx" +#include <ChartView.hxx> +#include <PopupRequest.hxx> +#include <ModifyListenerHelper.hxx> + +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <svl/numuno.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/document/DocumentProperties.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <com/sun/star/chart2/XChartTypeTemplate.hpp> + +#include <sal/log.hxx> +#include <svl/zforlist.hxx> +#include <tools/diagnose_ex.h> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Any; +using ::osl::MutexGuard; + +using namespace ::com::sun::star; +using namespace ::apphelper; +using namespace ::chart::CloneHelper; + +namespace +{ +const OUString lcl_aGDIMetaFileMIMEType( + "application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""); +const OUString lcl_aGDIMetaFileMIMETypeHighContrast( + "application/x-openoffice-highcontrast-gdimetafile;windows_formatname=\"GDIMetaFile\""); + +} // anonymous namespace + +// ChartModel Constructor and Destructor + +namespace chart +{ + +ChartModel::ChartModel(uno::Reference<uno::XComponentContext > const & xContext) + : m_aLifeTimeManager( this, this ) + , m_bReadOnly( false ) + , m_bModified( false ) + , m_nInLoad(0) + , m_bUpdateNotificationsPending(false) + , mbTimeBased(false) + , m_aControllers( m_aModelMutex ) + , m_nControllerLockCount(0) + , m_xContext( xContext ) + , m_aVisualAreaSize( ChartModelHelper::getDefaultPageSize() ) + , m_xPageBackground( new PageBackground ) + , m_xXMLNamespaceMap( createNameContainer( ::cppu::UnoType<OUString>::get(), + "com.sun.star.xml.NamespaceMap", "com.sun.star.comp.chart.XMLNameSpaceMap" ) ) + , mnStart(0) + , mnEnd(0) +{ + osl_atomic_increment(&m_refCount); + { + m_xOldModelAgg.set( + m_xContext->getServiceManager()->createInstanceWithContext( + CHART_CHARTAPIWRAPPER_SERVICE_NAME, + m_xContext ), uno::UNO_QUERY_THROW ); + m_xOldModelAgg->setDelegator( *this ); + } + + { + ModifyListenerHelper::addListener( m_xPageBackground, this ); + m_xChartTypeManager.set( xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.chart2.ChartTypeManager", m_xContext ), uno::UNO_QUERY ); + } + osl_atomic_decrement(&m_refCount); +} + +ChartModel::ChartModel( const ChartModel & rOther ) + : impl::ChartModel_Base(rOther) + , m_aLifeTimeManager( this, this ) + , m_bReadOnly( rOther.m_bReadOnly ) + , m_bModified( rOther.m_bModified ) + , m_nInLoad(0) + , m_bUpdateNotificationsPending(false) + , mbTimeBased(rOther.mbTimeBased) + , m_aResource( rOther.m_aResource ) + , m_aMediaDescriptor( rOther.m_aMediaDescriptor ) + , m_aControllers( m_aModelMutex ) + , m_nControllerLockCount(0) + , m_xContext( rOther.m_xContext ) + // @note: the old model aggregate must not be shared with other models if it + // is, you get mutex deadlocks + //, m_xOldModelAgg( nullptr ) //rOther.m_xOldModelAgg ) + // m_xStorage( nullptr ) //rOther.m_xStorage ) + , m_aVisualAreaSize( rOther.m_aVisualAreaSize ) + , m_aGraphicObjectVector( rOther.m_aGraphicObjectVector ) + , m_xDataProvider( rOther.m_xDataProvider ) + , m_xInternalDataProvider( rOther.m_xInternalDataProvider ) + , mnStart(rOther.mnStart) + , mnEnd(rOther.mnEnd) +{ + osl_atomic_increment(&m_refCount); + { + m_xOldModelAgg.set( + m_xContext->getServiceManager()->createInstanceWithContext( + CHART_CHARTAPIWRAPPER_SERVICE_NAME, + m_xContext ), uno::UNO_QUERY_THROW ); + m_xOldModelAgg->setDelegator( *this ); + + Reference< util::XModifyListener > xListener; + Reference< chart2::XTitle > xNewTitle = CreateRefClone< chart2::XTitle >()( rOther.m_xTitle ); + Reference< chart2::XDiagram > xNewDiagram = CreateRefClone< chart2::XDiagram >()( rOther.m_xDiagram ); + Reference< beans::XPropertySet > xNewPageBackground = CreateRefClone< beans::XPropertySet >()( rOther.m_xPageBackground ); + Reference< chart2::XChartTypeManager > xChartTypeManager = CreateRefClone< chart2::XChartTypeManager >()( rOther.m_xChartTypeManager ); + Reference< container::XNameAccess > xXMLNamespaceMap = CreateRefClone< container::XNameAccess >()( rOther.m_xXMLNamespaceMap ); + + { + MutexGuard aGuard( m_aModelMutex ); + xListener = this; + m_xTitle = xNewTitle; + m_xDiagram = xNewDiagram; + m_xPageBackground = xNewPageBackground; + m_xChartTypeManager = xChartTypeManager; + m_xXMLNamespaceMap = xXMLNamespaceMap; + } + + ModifyListenerHelper::addListener( xNewTitle, xListener ); + ModifyListenerHelper::addListener( xNewDiagram, xListener ); + ModifyListenerHelper::addListener( xNewPageBackground, xListener ); + xListener.clear(); + } + osl_atomic_decrement(&m_refCount); +} + +ChartModel::~ChartModel() +{ + if( m_xOldModelAgg.is()) + m_xOldModelAgg->setDelegator( nullptr ); +} + +void SAL_CALL ChartModel::initialize( const Sequence< Any >& /*rArguments*/ ) +{ + //#i113722# avoid duplicate creation + + //maybe additional todo?: + //support argument "EmbeddedObject"? + //support argument "EmbeddedScriptSupport"? + //support argument "DocumentRecoverySupport"? +} + +css::uno::Reference< css::uno::XInterface > ChartModel::getChartView() const +{ + return static_cast< ::cppu::OWeakObject* >( mxChartView.get() ); +} + +// private methods + +OUString ChartModel::impl_g_getLocation() +{ + + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return OUString(); //behave passive if already disposed or closed or throw exception @todo? + //mutex is acquired + return m_aResource; +} + +bool ChartModel::impl_isControllerConnected( const uno::Reference< frame::XController >& xController ) +{ + try + { + std::vector< uno::Reference<uno::XInterface> > aSeq = m_aControllers.getElements(); + for( const auto & r : aSeq ) + { + if( r == xController ) + return true; + } + } + catch (const uno::Exception&) + { + } + return false; +} + +uno::Reference< frame::XController > ChartModel::impl_getCurrentController() +{ + //@todo? hold only weak references to controllers + + // get the last active controller of this model + if( m_xCurrentController.is() ) + return m_xCurrentController; + + // get the first controller of this model + if( m_aControllers.getLength() ) + { + uno::Reference<uno::XInterface> xI = m_aControllers.getElements()[0]; + return uno::Reference<frame::XController>( xI, uno::UNO_QUERY ); + } + + //return nothing if no controllers are connected at all + return uno::Reference< frame::XController > (); +} + +void ChartModel::impl_notifyCloseListeners() +{ + ::cppu::OInterfaceContainerHelper* pIC = m_aLifeTimeManager.m_aListenerContainer + .getContainer( cppu::UnoType<util::XCloseListener>::get()); + if( pIC ) + { + lang::EventObject aEvent( static_cast< lang::XComponent*>(this) ); + ::cppu::OInterfaceIteratorHelper aIt( *pIC ); + while( aIt.hasMoreElements() ) + { + uno::Reference< util::XCloseListener > xListener( aIt.next(), uno::UNO_QUERY ); + if( xListener.is() ) + xListener->notifyClosing( aEvent ); + } + } +} + +void ChartModel::impl_adjustAdditionalShapesPositionAndSize( const awt::Size& aVisualAreaSize ) +{ + uno::Reference< beans::XPropertySet > xProperties( static_cast< ::cppu::OWeakObject* >( this ), uno::UNO_QUERY ); + if ( !xProperties.is() ) + return; + + uno::Reference< drawing::XShapes > xShapes; + xProperties->getPropertyValue( "AdditionalShapes" ) >>= xShapes; + if ( !xShapes.is() ) + return; + + sal_Int32 nCount = xShapes->getCount(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + Reference< drawing::XShape > xShape; + if ( xShapes->getByIndex( i ) >>= xShape ) + { + if ( xShape.is() ) + { + awt::Point aPos( xShape->getPosition() ); + awt::Size aSize( xShape->getSize() ); + + double fWidth = static_cast< double >( aVisualAreaSize.Width ) / m_aVisualAreaSize.Width; + double fHeight = static_cast< double >( aVisualAreaSize.Height ) / m_aVisualAreaSize.Height; + + aPos.X = static_cast< long >( aPos.X * fWidth ); + aPos.Y = static_cast< long >( aPos.Y * fHeight ); + aSize.Width = static_cast< long >( aSize.Width * fWidth ); + aSize.Height = static_cast< long >( aSize.Height * fHeight ); + + xShape->setPosition( aPos ); + xShape->setSize( aSize ); + } + } + } +} + +// lang::XServiceInfo + +OUString SAL_CALL ChartModel::getImplementationName() +{ + return CHART_MODEL_SERVICE_IMPLEMENTATION_NAME; +} + +sal_Bool SAL_CALL ChartModel::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL ChartModel::getSupportedServiceNames() +{ + return { + CHART_MODEL_SERVICE_NAME, + "com.sun.star.document.OfficeDocument", + "com.sun.star.chart.ChartDocument" + }; +} + +// frame::XModel (required interface) + +sal_Bool SAL_CALL ChartModel::attachResource( const OUString& rURL + , const uno::Sequence< beans::PropertyValue >& rMediaDescriptor ) +{ + /* + The method attachResource() is used by the frame loader implementations + to inform the model about its URL and MediaDescriptor. + */ + + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return false; //behave passive if already disposed or closed or throw exception @todo? + //mutex is acquired + + if(!m_aResource.isEmpty())//we have a resource already //@todo? or is setting a new resource allowed? + return false; + m_aResource = rURL; + m_aMediaDescriptor = rMediaDescriptor; + + //@todo ? check rURL ?? + //@todo ? evaluate m_aMediaDescriptor; + //@todo ? ... ??? --> nothing, this method is only for setting information + + return true; +} + +OUString SAL_CALL ChartModel::getURL() +{ + return impl_g_getLocation(); +} + +uno::Sequence< beans::PropertyValue > SAL_CALL ChartModel::getArgs() +{ + /* + The method getArgs() returns a sequence of property values + that report the resource description according to com.sun.star.document.MediaDescriptor, + specified on loading or saving with storeAsURL. + */ + + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return uno::Sequence< beans::PropertyValue >(); //behave passive if already disposed or closed or throw exception @todo? + //mutex is acquired + + return m_aMediaDescriptor; +} + +void SAL_CALL ChartModel::connectController( const uno::Reference< frame::XController >& xController ) +{ + //@todo? this method is declared as oneway -> ...? + + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return ; //behave passive if already disposed or closed + //mutex is acquired + + //--add controller + m_aControllers.addInterface(xController); +} + +void SAL_CALL ChartModel::disconnectController( const uno::Reference< frame::XController >& xController ) +{ + //@todo? this method is declared as oneway -> ...? + + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return; //behave passive if already disposed or closed + + //--remove controller + m_aControllers.removeInterface(xController); + + //case: current controller is disconnected: + if( m_xCurrentController == xController ) + m_xCurrentController.clear(); + + DisposeHelper::DisposeAndClear( m_xRangeHighlighter ); + DisposeHelper::DisposeAndClear(m_xPopupRequest); +} + +void SAL_CALL ChartModel::lockControllers() +{ + /* + suspends some notifications to the controllers which are used for display updates. + + The calls to lockControllers() and unlockControllers() may be nested + and even overlapping, but they must be in pairs. While there is at least one lock + remaining, some notifications for display updates are not broadcasted. + */ + + //@todo? this method is declared as oneway -> ...? + + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return; //behave passive if already disposed or closed or throw exception @todo? + ++m_nControllerLockCount; +} + +void SAL_CALL ChartModel::unlockControllers() +{ + /* + resumes the notifications which were suspended by lockControllers() . + + The calls to lockControllers() and unlockControllers() may be nested + and even overlapping, but they must be in pairs. While there is at least one lock + remaining, some notifications for display updates are not broadcasted. + */ + + //@todo? this method is declared as oneway -> ...? + + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return; //behave passive if already disposed or closed or throw exception @todo? + if( m_nControllerLockCount == 0 ) + { + SAL_WARN("chart2", "ChartModel: unlockControllers called with m_nControllerLockCount == 0" ); + return; + } + --m_nControllerLockCount; + if( m_nControllerLockCount == 0 && m_bUpdateNotificationsPending ) + { + aGuard.clear(); + impl_notifyModifiedListeners(); + } +} + +sal_Bool SAL_CALL ChartModel::hasControllersLocked() +{ + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + return false; //behave passive if already disposed or closed or throw exception @todo? + return ( m_nControllerLockCount != 0 ) ; +} + +uno::Reference< frame::XController > SAL_CALL ChartModel::getCurrentController() +{ + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + throw lang::DisposedException( + "getCurrentController was called on an already disposed or closed model", + static_cast< ::cppu::OWeakObject* >(this) ); + + return impl_getCurrentController(); +} + +void SAL_CALL ChartModel::setCurrentController( const uno::Reference< frame::XController >& xController ) +{ + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + throw lang::DisposedException( + "setCurrentController was called on an already disposed or closed model", + static_cast< ::cppu::OWeakObject* >(this) ); + + //OSL_ENSURE( impl_isControllerConnected(xController), "setCurrentController is called with a Controller which is not connected" ); + if(!impl_isControllerConnected(xController)) + throw container::NoSuchElementException( + "setCurrentController is called with a Controller which is not connected", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_xCurrentController = xController; + + DisposeHelper::DisposeAndClear( m_xRangeHighlighter ); + DisposeHelper::DisposeAndClear(m_xPopupRequest); +} + +uno::Reference< uno::XInterface > SAL_CALL ChartModel::getCurrentSelection() +{ + LifeTimeGuard aGuard(m_aLifeTimeManager); + if(!aGuard.startApiCall()) + throw lang::DisposedException( + "getCurrentSelection was called on an already disposed or closed model", + static_cast< ::cppu::OWeakObject* >(this) ); + + uno::Reference< uno::XInterface > xReturn; + uno::Reference< frame::XController > xController = impl_getCurrentController(); + + aGuard.clear(); + if( xController.is() ) + { + uno::Reference< view::XSelectionSupplier > xSelectionSupl( xController, uno::UNO_QUERY ); + if ( xSelectionSupl.is() ) + { + uno::Any aSel = xSelectionSupl->getSelection(); + OUString aObjectCID; + if( aSel >>= aObjectCID ) + xReturn.set( ObjectIdentifier::getObjectPropertySet( aObjectCID, Reference< XChartDocument >(this))); + } + } + return xReturn; +} + +// lang::XComponent (base of XModel) +void SAL_CALL ChartModel::dispose() +{ + Reference< XInterface > xKeepAlive( *this ); + + //This object should release all resources and references in the + //easiest possible manner + //This object must notify all registered listeners using the method + //<member>XEventListener::disposing</member> + + //hold no mutex + if( !m_aLifeTimeManager.dispose() ) + return; + + //--release all resources and references + //// @todo + + if ( m_xDiagram.is() ) + ModifyListenerHelper::removeListener( m_xDiagram, this ); + + if ( m_xDataProvider.is() ) + { + Reference<util::XModifyBroadcaster> xModifyBroadcaster( m_xDataProvider, uno::UNO_QUERY ); + if ( xModifyBroadcaster.is() ) + xModifyBroadcaster->removeModifyListener( this ); + } + + m_xDataProvider.clear(); + m_xInternalDataProvider.clear(); + m_xNumberFormatsSupplier.clear(); + DisposeHelper::DisposeAndClear( m_xOwnNumberFormatsSupplier ); + DisposeHelper::DisposeAndClear( m_xChartTypeManager ); + DisposeHelper::DisposeAndClear( m_xDiagram ); + DisposeHelper::DisposeAndClear( m_xTitle ); + DisposeHelper::DisposeAndClear( m_xPageBackground ); + DisposeHelper::DisposeAndClear( m_xXMLNamespaceMap ); + + m_xStorage.clear(); + // just clear, don't dispose - we're not the owner + + if ( m_pUndoManager.is() ) + m_pUndoManager->disposing(); + m_pUndoManager.clear(); + // that's important, since the UndoManager implementation delegates its ref counting to ourself. + + if( m_xOldModelAgg.is()) // #i120828#, to release cyclic reference to ChartModel object + m_xOldModelAgg->setDelegator( nullptr ); + + m_aControllers.disposeAndClear( lang::EventObject( static_cast< cppu::OWeakObject * >( this ))); + m_xCurrentController.clear(); + + DisposeHelper::DisposeAndClear( m_xRangeHighlighter ); + DisposeHelper::DisposeAndClear(m_xPopupRequest); + + if( m_xOldModelAgg.is()) + m_xOldModelAgg->setDelegator( nullptr ); +} + +void SAL_CALL ChartModel::addEventListener( const uno::Reference< lang::XEventListener > & xListener ) +{ + if( m_aLifeTimeManager.impl_isDisposedOrClosed() ) + return; //behave passive if already disposed or closed + + m_aLifeTimeManager.m_aListenerContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + +void SAL_CALL ChartModel::removeEventListener( const uno::Reference< lang::XEventListener > & xListener ) +{ + if( m_aLifeTimeManager.impl_isDisposedOrClosed(false) ) + return; //behave passive if already disposed or closed + + m_aLifeTimeManager.m_aListenerContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + +// util::XCloseBroadcaster (base of XCloseable) +void SAL_CALL ChartModel::addCloseListener( const uno::Reference< util::XCloseListener > & xListener ) +{ + m_aLifeTimeManager.g_addCloseListener( xListener ); +} + +void SAL_CALL ChartModel::removeCloseListener( const uno::Reference< util::XCloseListener > & xListener ) +{ + if( m_aLifeTimeManager.impl_isDisposedOrClosed(false) ) + return; //behave passive if already disposed or closed + + m_aLifeTimeManager.m_aListenerContainer.removeInterface( cppu::UnoType<util::XCloseListener>::get(), xListener ); +} + +// util::XCloseable +void SAL_CALL ChartModel::close( sal_Bool bDeliverOwnership ) +{ + //hold no mutex + + if( !m_aLifeTimeManager.g_close_startTryClose( bDeliverOwnership ) ) + return; + //no mutex is acquired + + // At the end of this method may we must dispose ourself ... + // and may nobody from outside hold a reference to us ... + // then it's a good idea to do that by ourself. + uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) ); + + //the listeners have had no veto + //check whether we self can close + { + util::CloseVetoException aVetoException( + "the model itself could not be closed", + static_cast< ::cppu::OWeakObject* >(this) ); + + m_aLifeTimeManager.g_close_isNeedToCancelLongLastingCalls( bDeliverOwnership, aVetoException ); + } + m_aLifeTimeManager.g_close_endTryClose_doClose(); + + // BM @todo: is it ok to call the listeners here? + impl_notifyCloseListeners(); +} + +// lang::XTypeProvider +uno::Sequence< uno::Type > SAL_CALL ChartModel::getTypes() +{ + uno::Reference< lang::XTypeProvider > xAggTypeProvider; + if( (m_xOldModelAgg->queryAggregation( cppu::UnoType<decltype(xAggTypeProvider)>::get()) >>= xAggTypeProvider) + && xAggTypeProvider.is()) + { + return comphelper::concatSequences( + impl::ChartModel_Base::getTypes(), + xAggTypeProvider->getTypes()); + } + + return impl::ChartModel_Base::getTypes(); +} + +// document::XDocumentPropertiesSupplier +uno::Reference< document::XDocumentProperties > SAL_CALL + ChartModel::getDocumentProperties() +{ + ::osl::MutexGuard aGuard( m_aModelMutex ); + if ( !m_xDocumentProperties.is() ) + { + m_xDocumentProperties.set( document::DocumentProperties::create( ::comphelper::getProcessComponentContext() ) ); + } + return m_xDocumentProperties; +} + +// document::XDocumentPropertiesSupplier +Reference< document::XUndoManager > SAL_CALL ChartModel::getUndoManager( ) +{ + ::osl::MutexGuard aGuard( m_aModelMutex ); + if ( !m_pUndoManager.is() ) + m_pUndoManager.set( new UndoManager( *this, m_aModelMutex ) ); + return m_pUndoManager.get(); +} + +// chart2::XChartDocument + +uno::Reference< chart2::XDiagram > SAL_CALL ChartModel::getFirstDiagram() +{ + MutexGuard aGuard( m_aModelMutex ); + return m_xDiagram; +} + +void SAL_CALL ChartModel::setFirstDiagram( const uno::Reference< chart2::XDiagram >& xDiagram ) +{ + Reference< chart2::XDiagram > xOldDiagram; + Reference< util::XModifyListener > xListener; + { + MutexGuard aGuard( m_aModelMutex ); + if( xDiagram == m_xDiagram ) + return; + xOldDiagram = m_xDiagram; + m_xDiagram = xDiagram; + xListener = this; + } + //don't keep the mutex locked while calling out + ModifyListenerHelper::removeListener( xOldDiagram, xListener ); + ModifyListenerHelper::addListener( xDiagram, xListener ); + setModified( true ); +} + +Reference< chart2::data::XDataSource > ChartModel::impl_createDefaultData() +{ + Reference< chart2::data::XDataSource > xDataSource; + if( hasInternalDataProvider() ) + { + uno::Reference< lang::XInitialization > xIni(m_xInternalDataProvider,uno::UNO_QUERY); + if( xIni.is() ) + { + //init internal dataprovider + { + uno::Sequence< uno::Any > aArgs(1); + beans::NamedValue aParam( "CreateDefaultData" ,uno::Any(true) ); + aArgs[0] <<= aParam; + xIni->initialize(aArgs); + } + //create data + uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({ + { "CellRangeRepresentation", uno::Any( OUString("all") ) }, + { "HasCategories", uno::Any( true ) }, + { "FirstCellAsLabel", uno::Any( true ) }, + { "DataRowSource", uno::Any( css::chart::ChartDataRowSource_COLUMNS ) } + })); + xDataSource = m_xInternalDataProvider->createDataSource( aArgs ); + } + } + return xDataSource; +} + +void SAL_CALL ChartModel::createInternalDataProvider( sal_Bool bCloneExistingData ) +{ + // don't lock the mutex, because this call calls out to code that tries to + // lock the solar mutex. On the other hand, a paint locks the solar mutex + // and calls to the model lock the model's mutex => deadlock + // @todo: lock a separate mutex in the InternalData class + if( !hasInternalDataProvider() ) + { + if( bCloneExistingData ) + m_xInternalDataProvider = ChartModelHelper::createInternalDataProvider( this, true ); + else + m_xInternalDataProvider = ChartModelHelper::createInternalDataProvider( Reference<XChartDocument>(), true ); + m_xDataProvider.set( m_xInternalDataProvider ); + } + setModified( true ); +} + +void ChartModel::removeDataProviders() +{ + if (m_xInternalDataProvider.is()) + m_xInternalDataProvider.clear(); + if (m_xDataProvider.is()) + m_xDataProvider.clear(); +} + +sal_Bool SAL_CALL ChartModel::hasInternalDataProvider() +{ + return m_xDataProvider.is() && m_xInternalDataProvider.is(); +} + +uno::Reference< chart2::data::XDataProvider > SAL_CALL ChartModel::getDataProvider() +{ + MutexGuard aGuard( m_aModelMutex ); + return m_xDataProvider; +} + +// ____ XDataReceiver ____ + +void SAL_CALL ChartModel::attachDataProvider( const uno::Reference< chart2::data::XDataProvider >& xDataProvider ) +{ + { + MutexGuard aGuard( m_aModelMutex ); + uno::Reference< beans::XPropertySet > xProp( xDataProvider, uno::UNO_QUERY ); + if( xProp.is() ) + { + try + { + bool bIncludeHiddenCells = ChartModelHelper::isIncludeHiddenCells( Reference< frame::XModel >(this) ); + xProp->setPropertyValue("IncludeHiddenCells", uno::Any(bIncludeHiddenCells)); + } + catch (const beans::UnknownPropertyException&) + { + } + } + + uno::Reference<util::XModifyBroadcaster> xModifyBroadcaster(xDataProvider, uno::UNO_QUERY); + if (xModifyBroadcaster.is()) + { + xModifyBroadcaster->addModifyListener(this); + } + + m_xDataProvider.set( xDataProvider ); + m_xInternalDataProvider.clear(); + + //the numberformatter is kept independent of the data provider! + } + setModified( true ); +} + +void SAL_CALL ChartModel::attachNumberFormatsSupplier( const uno::Reference< util::XNumberFormatsSupplier >& xNewSupplier ) +{ + { + MutexGuard aGuard( m_aModelMutex ); + if( xNewSupplier==m_xNumberFormatsSupplier ) + return; + if( xNewSupplier==m_xOwnNumberFormatsSupplier ) + return; + if( m_xOwnNumberFormatsSupplier.is() && xNewSupplier.is() ) + { + //@todo + //merge missing numberformats from own to new formatter + } + else if( !xNewSupplier.is() ) + { + if( m_xNumberFormatsSupplier.is() ) + { + //@todo + //merge missing numberformats from old numberformatter to own numberformatter + //create own numberformatter if necessary + } + } + + m_xNumberFormatsSupplier.set( xNewSupplier ); + m_xOwnNumberFormatsSupplier.clear(); + } + setModified( true ); +} + +void SAL_CALL ChartModel::setArguments( const Sequence< beans::PropertyValue >& aArguments ) +{ + { + MutexGuard aGuard( m_aModelMutex ); + if( !m_xDataProvider.is() ) + return; + lockControllers(); + + try + { + Reference< chart2::data::XDataSource > xDataSource( m_xDataProvider->createDataSource( aArguments ) ); + if( xDataSource.is() ) + { + Reference< chart2::XDiagram > xDia( getFirstDiagram() ); + if( !xDia.is() ) + { + Reference< chart2::XChartTypeTemplate > xTemplate( impl_createDefaultChartTypeTemplate() ); + if( xTemplate.is()) + setFirstDiagram( xTemplate->createDiagramByDataSource( xDataSource, aArguments ) ); + } + else + xDia->setDiagramData( xDataSource, aArguments ); + } + } + catch (const lang::IllegalArgumentException&) + { + throw; + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + unlockControllers(); + } + setModified( true ); +} + +Sequence< OUString > SAL_CALL ChartModel::getUsedRangeRepresentations() +{ + return DataSourceHelper::getUsedDataRanges( Reference< frame::XModel >(this)); +} + +Reference< chart2::data::XDataSource > SAL_CALL ChartModel::getUsedData() +{ + return DataSourceHelper::getUsedData( Reference< chart2::XChartDocument >(this)); +} + +Reference< chart2::data::XRangeHighlighter > SAL_CALL ChartModel::getRangeHighlighter() +{ + if( ! m_xRangeHighlighter.is()) + { + uno::Reference< view::XSelectionSupplier > xSelSupp( getCurrentController(), uno::UNO_QUERY ); + if( xSelSupp.is() ) + m_xRangeHighlighter.set( ChartModelHelper::createRangeHighlighter( xSelSupp )); + } + return m_xRangeHighlighter; +} + +Reference<awt::XRequestCallback> SAL_CALL ChartModel::getPopupRequest() +{ + if (!m_xPopupRequest.is()) + m_xPopupRequest.set(new PopupRequest); + return m_xPopupRequest; +} + +Reference< chart2::XChartTypeTemplate > ChartModel::impl_createDefaultChartTypeTemplate() +{ + Reference< chart2::XChartTypeTemplate > xTemplate; + Reference< lang::XMultiServiceFactory > xFact( m_xChartTypeManager, uno::UNO_QUERY ); + if( xFact.is() ) + xTemplate.set( xFact->createInstance( "com.sun.star.chart2.template.Column" ), uno::UNO_QUERY ); + return xTemplate; +} + +void SAL_CALL ChartModel::setChartTypeManager( const uno::Reference< chart2::XChartTypeManager >& xNewManager ) +{ + { + MutexGuard aGuard( m_aModelMutex ); + m_xChartTypeManager = xNewManager; + } + setModified( true ); +} + +uno::Reference< chart2::XChartTypeManager > SAL_CALL ChartModel::getChartTypeManager() +{ + MutexGuard aGuard( m_aModelMutex ); + return m_xChartTypeManager; +} + +uno::Reference< beans::XPropertySet > SAL_CALL ChartModel::getPageBackground() +{ + MutexGuard aGuard( m_aModelMutex ); + return m_xPageBackground; +} + +void SAL_CALL ChartModel::createDefaultChart() +{ + insertDefaultChart(); +} + +// ____ XTitled ____ +uno::Reference< chart2::XTitle > SAL_CALL ChartModel::getTitleObject() +{ + MutexGuard aGuard( m_aModelMutex ); + return m_xTitle; +} + +void SAL_CALL ChartModel::setTitleObject( const uno::Reference< chart2::XTitle >& xTitle ) +{ + { + MutexGuard aGuard( m_aModelMutex ); + if( m_xTitle.is() ) + ModifyListenerHelper::removeListener( m_xTitle, this ); + m_xTitle = xTitle; + ModifyListenerHelper::addListener( m_xTitle, this ); + } + setModified( true ); +} + +// ____ XInterface (for old API wrapper) ____ +uno::Any SAL_CALL ChartModel::queryInterface( const uno::Type& aType ) +{ + uno::Any aResult( impl::ChartModel_Base::queryInterface( aType )); + + if( ! aResult.hasValue()) + { + // try old API wrapper + try + { + if( m_xOldModelAgg.is()) + aResult = m_xOldModelAgg->queryAggregation( aType ); + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + } + + return aResult; +} + +// ____ XCloneable ____ +Reference< util::XCloneable > SAL_CALL ChartModel::createClone() +{ + return Reference< util::XCloneable >( new ChartModel( *this )); +} + +// ____ XVisualObject ____ +void SAL_CALL ChartModel::setVisualAreaSize( ::sal_Int64 nAspect, const awt::Size& aSize ) +{ + if( nAspect == embed::Aspects::MSOLE_CONTENT ) + { + ControllerLockGuard aLockGuard( *this ); + bool bChanged = + (m_aVisualAreaSize.Width != aSize.Width || + m_aVisualAreaSize.Height != aSize.Height); + + // #i12587# support for shapes in chart + if ( bChanged ) + { + impl_adjustAdditionalShapesPositionAndSize( aSize ); + } + + m_aVisualAreaSize = aSize; + if( bChanged ) + setModified( true ); + } + else + { + OSL_FAIL( "setVisualAreaSize: Aspect not implemented yet."); + } +} + +awt::Size SAL_CALL ChartModel::getVisualAreaSize( ::sal_Int64 nAspect ) +{ + OSL_ENSURE( nAspect == embed::Aspects::MSOLE_CONTENT, + "No aspects other than content are supported" ); + // other possible aspects are MSOLE_THUMBNAIL, MSOLE_ICON and MSOLE_DOCPRINT + + return m_aVisualAreaSize; +} + +embed::VisualRepresentation SAL_CALL ChartModel::getPreferredVisualRepresentation( ::sal_Int64 nAspect ) +{ + OSL_ENSURE( nAspect == embed::Aspects::MSOLE_CONTENT, + "No aspects other than content are supported" ); + + embed::VisualRepresentation aResult; + + try + { + Sequence< sal_Int8 > aMetafile; + + //get view from old api wrapper + Reference< datatransfer::XTransferable > xTransferable( + createInstance( CHART_VIEW_SERVICE_NAME ), uno::UNO_QUERY ); + if( xTransferable.is() ) + { + datatransfer::DataFlavor aDataFlavor( lcl_aGDIMetaFileMIMEType, + "GDIMetaFile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + uno::Any aData( xTransferable->getTransferData( aDataFlavor ) ); + aData >>= aMetafile; + } + + aResult.Flavor.MimeType = lcl_aGDIMetaFileMIMEType; + aResult.Flavor.DataType = cppu::UnoType<decltype(aMetafile)>::get(); + + aResult.Data <<= aMetafile; + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + + return aResult; +} + +::sal_Int32 SAL_CALL ChartModel::getMapUnit( ::sal_Int64 nAspect ) +{ + OSL_ENSURE( nAspect == embed::Aspects::MSOLE_CONTENT, + "No aspects other than content are supported" ); + return embed::EmbedMapUnits::ONE_100TH_MM; +} + +// ____ datatransfer::XTransferable ____ +uno::Any SAL_CALL ChartModel::getTransferData( const datatransfer::DataFlavor& aFlavor ) +{ + uno::Any aResult; + if( !isDataFlavorSupported( aFlavor ) ) + throw datatransfer::UnsupportedFlavorException( + aFlavor.MimeType, static_cast< ::cppu::OWeakObject* >( this )); + + try + { + //get view from old api wrapper + Reference< datatransfer::XTransferable > xTransferable( + createInstance( CHART_VIEW_SERVICE_NAME ), uno::UNO_QUERY ); + if( xTransferable.is() && + xTransferable->isDataFlavorSupported( aFlavor )) + { + aResult = xTransferable->getTransferData( aFlavor ); + } + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("chart2"); + } + + return aResult; +} + +Sequence< datatransfer::DataFlavor > SAL_CALL ChartModel::getTransferDataFlavors() +{ + uno::Sequence< datatransfer::DataFlavor > aRet(1); + + aRet[0] = datatransfer::DataFlavor( lcl_aGDIMetaFileMIMETypeHighContrast, + "GDIMetaFile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ); + + return aRet; +} + +sal_Bool SAL_CALL ChartModel::isDataFlavorSupported( const datatransfer::DataFlavor& aFlavor ) +{ + return aFlavor.MimeType == lcl_aGDIMetaFileMIMETypeHighContrast; +} + +namespace +{ +enum eServiceType +{ + SERVICE_DASH_TABLE, + SERVICE_GRADIENT_TABLE, + SERVICE_HATCH_TABLE, + SERVICE_BITMAP_TABLE, + SERVICE_TRANSP_GRADIENT_TABLE, + SERVICE_MARKER_TABLE, + SERVICE_NAMESPACE_MAP +}; + +typedef std::map< OUString, enum eServiceType > tServiceNameMap; + +tServiceNameMap & lcl_getStaticServiceNameMap() +{ + static tServiceNameMap aServiceNameMap{ + {"com.sun.star.drawing.DashTable", SERVICE_DASH_TABLE}, + {"com.sun.star.drawing.GradientTable", SERVICE_GRADIENT_TABLE}, + {"com.sun.star.drawing.HatchTable", SERVICE_HATCH_TABLE}, + {"com.sun.star.drawing.BitmapTable", SERVICE_BITMAP_TABLE}, + {"com.sun.star.drawing.TransparencyGradientTable", SERVICE_TRANSP_GRADIENT_TABLE}, + {"com.sun.star.drawing.MarkerTable", SERVICE_MARKER_TABLE}, + {"com.sun.star.xml.NamespaceMap", SERVICE_NAMESPACE_MAP}}; + return aServiceNameMap; +} +} +// ____ XMultiServiceFactory ____ +Reference< uno::XInterface > SAL_CALL ChartModel::createInstance( const OUString& rServiceSpecifier ) +{ + tServiceNameMap & rMap = lcl_getStaticServiceNameMap(); + + tServiceNameMap::const_iterator aIt( rMap.find( rServiceSpecifier )); + if( aIt != rMap.end()) + { + switch( (*aIt).second ) + { + case SERVICE_DASH_TABLE: + case SERVICE_GRADIENT_TABLE: + case SERVICE_HATCH_TABLE: + case SERVICE_BITMAP_TABLE: + case SERVICE_TRANSP_GRADIENT_TABLE: + case SERVICE_MARKER_TABLE: + { + if(!mxChartView.is()) + { + mxChartView = new ChartView( m_xContext, *this); + } + return mxChartView->createInstance( rServiceSpecifier ); + } + break; + case SERVICE_NAMESPACE_MAP: + return Reference< uno::XInterface >( m_xXMLNamespaceMap ); + } + } + else if(rServiceSpecifier == CHART_VIEW_SERVICE_NAME) + { + if(!mxChartView.is()) + { + mxChartView = new ChartView( m_xContext, *this); + } + + return static_cast< ::cppu::OWeakObject* >( mxChartView.get() ); + } + else + { + if( m_xOldModelAgg.is() ) + { + Any aAny = m_xOldModelAgg->queryAggregation( cppu::UnoType<lang::XMultiServiceFactory>::get()); + uno::Reference< lang::XMultiServiceFactory > xOldModelFactory; + if( (aAny >>= xOldModelFactory) && xOldModelFactory.is() ) + { + return xOldModelFactory->createInstance( rServiceSpecifier ); + } + } + } + return nullptr; +} + +Reference< uno::XInterface > SAL_CALL ChartModel::createInstanceWithArguments( + const OUString& rServiceSpecifier , const Sequence< Any >& Arguments ) +{ + OSL_ENSURE( Arguments.hasElements(), "createInstanceWithArguments: Warning: Arguments are ignored" ); + return createInstance( rServiceSpecifier ); +} + +Sequence< OUString > SAL_CALL ChartModel::getAvailableServiceNames() +{ + uno::Sequence< OUString > aResult; + + if( m_xOldModelAgg.is()) + { + Any aAny = m_xOldModelAgg->queryAggregation( cppu::UnoType<lang::XMultiServiceFactory>::get()); + uno::Reference< lang::XMultiServiceFactory > xOldModelFactory; + if( (aAny >>= xOldModelFactory) && xOldModelFactory.is() ) + { + return xOldModelFactory->getAvailableServiceNames(); + } + } + return aResult; +} + +Reference< util::XNumberFormatsSupplier > const & ChartModel::getNumberFormatsSupplier() +{ + if( !m_xNumberFormatsSupplier.is() ) + { + if( !m_xOwnNumberFormatsSupplier.is() ) + { + m_apSvNumberFormatter.reset( new SvNumberFormatter( m_xContext, LANGUAGE_SYSTEM ) ); + m_xOwnNumberFormatsSupplier = new SvNumberFormatsSupplierObj( m_apSvNumberFormatter.get() ); + //pOwnNumberFormatter->ChangeStandardPrec( 15 ); todo? + } + m_xNumberFormatsSupplier = m_xOwnNumberFormatsSupplier; + } + return m_xNumberFormatsSupplier; +} + +// ____ XUnoTunnel ___ +::sal_Int64 SAL_CALL ChartModel::getSomething( const Sequence< ::sal_Int8 >& aIdentifier ) +{ + if( isUnoTunnelId<SvNumberFormatsSupplierObj>(aIdentifier) ) + { + Reference< lang::XUnoTunnel > xTunnel( getNumberFormatsSupplier(), uno::UNO_QUERY ); + if( xTunnel.is() ) + return xTunnel->getSomething( aIdentifier ); + } + return 0; +} + +// ____ XNumberFormatsSupplier ____ +uno::Reference< beans::XPropertySet > SAL_CALL ChartModel::getNumberFormatSettings() +{ + Reference< util::XNumberFormatsSupplier > xSupplier( getNumberFormatsSupplier() ); + if( xSupplier.is() ) + return xSupplier->getNumberFormatSettings(); + return uno::Reference< beans::XPropertySet >(); +} + +uno::Reference< util::XNumberFormats > SAL_CALL ChartModel::getNumberFormats() +{ + Reference< util::XNumberFormatsSupplier > xSupplier( getNumberFormatsSupplier() ); + if( xSupplier.is() ) + return xSupplier->getNumberFormats(); + return uno::Reference< util::XNumberFormats >(); +} + +// ____ XChild ____ +Reference< uno::XInterface > SAL_CALL ChartModel::getParent() +{ + return Reference< uno::XInterface >(m_xParent,uno::UNO_QUERY); +} + +void SAL_CALL ChartModel::setParent( const Reference< uno::XInterface >& Parent ) +{ + if( Parent != m_xParent ) + m_xParent.set( Parent, uno::UNO_QUERY ); +} + +// ____ XDataSource ____ +uno::Sequence< Reference< chart2::data::XLabeledDataSequence > > SAL_CALL ChartModel::getDataSequences() +{ + Reference< chart2::data::XDataSource > xSource( + DataSourceHelper::getUsedData( uno::Reference< frame::XModel >(this) ) ); + if( xSource.is()) + return xSource->getDataSequences(); + + return uno::Sequence< Reference< chart2::data::XLabeledDataSequence > >(); +} + +//XDumper +OUString SAL_CALL ChartModel::dump() +{ + uno::Reference< qa::XDumper > xDumper( + createInstance( CHART_VIEW_SERVICE_NAME ), uno::UNO_QUERY ); + if (xDumper.is()) + return xDumper->dump(); + + return OUString(); +} + +void ChartModel::setTimeBasedRange(sal_Int32 nStart, sal_Int32 nEnd) +{ + mnStart = nStart; + mnEnd = nEnd; + mbTimeBased = true; +} + +void ChartModel::update() +{ + if(!mxChartView.is()) + { + mxChartView = new ChartView( m_xContext, *this); + } + mxChartView->setViewDirty(); + mxChartView->update(); +} + +bool ChartModel::isDataFromSpreadsheet() +{ + return !isDataFromPivotTable() && !hasInternalDataProvider(); +} + +bool ChartModel::isDataFromPivotTable() const +{ + uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(m_xDataProvider, uno::UNO_QUERY); + return xPivotTableDataProvider.is(); +} + +} // namespace chart + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_chart2_ChartModel_get_implementation(css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ::chart::ChartModel(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |