diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /chart2/source/model/main/ChartModel.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'chart2/source/model/main/ChartModel.cxx')
-rw-r--r-- | chart2/source/model/main/ChartModel.cxx | 1306 |
1 files changed, 1306 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..712be3fdf --- /dev/null +++ b/chart2/source/model/main/ChartModel.cxx @@ -0,0 +1,1306 @@ +/* -*- 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 <ChartTypeManager.hxx> +#include <ChartTypeTemplate.hxx> +#include <servicenames.hxx> +#include <DataSource.hxx> +#include <DataSourceHelper.hxx> +#include <ChartModelHelper.hxx> +#include <DisposeHelper.hxx> +#include <ControllerLockGuard.hxx> +#include <InternalDataProvider.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 <Diagram.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/numformat.hxx> +#include <svl/numuno.hxx> +#include <com/sun/star/lang/DisposedException.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 <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <libxml/xmlwriter.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 +{ +constexpr OUStringLiteral lcl_aGDIMetaFileMIMEType( + u"application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""); +constexpr OUStringLiteral lcl_aGDIMetaFileMIMETypeHighContrast( + u"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( new NameContainer() ) + , 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 ); + } + + { + m_xPageBackground->addModifyListener( this ); + m_xChartTypeManager = new ::chart::ChartTypeManager( m_xContext ); + } + 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 ); + rtl::Reference< ::chart::Diagram > xNewDiagram; + if (rOther.m_xDiagram.is()) + xNewDiagram = new ::chart::Diagram( *rOther.m_xDiagram ); + rtl::Reference< ::chart::PageBackground > xNewPageBackground = new PageBackground( *rOther.m_xPageBackground ); + rtl::Reference< ::chart::ChartTypeManager > xChartTypeManager; // does not implement XCloneable + rtl::Reference< ::chart::NameContainer > xXMLNamespaceMap = new NameContainer( *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 ); + if( xNewDiagram && xListener) + xNewDiagram->addModifyListener( xListener ); + if( xNewPageBackground && xListener) + xNewPageBackground->addModifyListener( 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"? +} + +ChartView* ChartModel::getChartView() const +{ + return 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.getInterface(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() +{ + ::comphelper::OInterfaceContainerHelper2* pIC = m_aLifeTimeManager.m_aListenerContainer + .getContainer( cppu::UnoType<util::XCloseListener>::get()); + if( pIC ) + { + lang::EventObject aEvent( static_cast< lang::XComponent*>(this) ); + ::comphelper::OInterfaceIteratorHelper2 aIt( *pIC ); + while( aIt.hasMoreElements() ) + { + static_cast< util::XCloseListener* >( aIt.next() )->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< tools::Long >( aPos.X * fWidth ); + aPos.Y = static_cast< tools::Long >( aPos.Y * fHeight ); + aSize.Width = static_cast< tools::Long >( aSize.Width * fWidth ); + aSize.Height = static_cast< tools::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, 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() ) + m_xDiagram->removeModifyListener( 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(); + m_xOwnNumberFormatsSupplier.clear(); + m_xChartTypeManager.clear(); + m_xDiagram.clear(); + DisposeHelper::DisposeAndClear( m_xTitle ); + m_xPageBackground.clear(); + m_xXMLNamespaceMap.clear(); + + 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; +} + +// 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 ) +{ + rtl::Reference< ::chart::Diagram > xOldDiagram; + Reference< util::XModifyListener > xListener; + { + MutexGuard aGuard( m_aModelMutex ); + if( xDiagram.get() == m_xDiagram.get() ) + return; + xOldDiagram = m_xDiagram; + assert(!xDiagram || dynamic_cast<::chart::Diagram*>(xDiagram.get())); + m_xDiagram = dynamic_cast<::chart::Diagram*>(xDiagram.get()); + xListener = this; + } + //don't keep the mutex locked while calling out + if( xOldDiagram && xListener ) + xOldDiagram->removeModifyListener( xListener ); + ModifyListenerHelper::addListener( xDiagram, xListener ); + setModified( true ); +} + +Reference< chart2::data::XDataSource > ChartModel::impl_createDefaultData() +{ + Reference< chart2::data::XDataSource > xDataSource; + if( hasInternalDataProvider() ) + { + //init internal dataprovider + { + beans::NamedValue aParam( "CreateDefaultData" ,uno::Any(true) ); + uno::Sequence< uno::Any > aArgs{ uno::Any(aParam) }; + m_xInternalDataProvider->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( nullptr, 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(); +} + +void ChartModel::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ChartModel")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + if (mxChartView.is()) + { + mxChartView->dumpAsXml(pWriter); + } + + (void)xmlTextWriterEndElement(pWriter); +} + +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( 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 == uno::Reference<XNumberFormatsSupplier>(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() ) + { + rtl::Reference< Diagram > xDia = getFirstChartDiagram(); + if( !xDia.is() ) + { + rtl::Reference< ::chart::ChartTypeTemplate > 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( this ); +} + +Reference< chart2::data::XDataSource > SAL_CALL ChartModel::getUsedData() +{ + return DataSourceHelper::getUsedData( *this ); +} + +Reference< chart2::data::XRangeHighlighter > SAL_CALL ChartModel::getRangeHighlighter() +{ + if( ! m_xRangeHighlighter.is()) + m_xRangeHighlighter.set( ChartModelHelper::createRangeHighlighter( this )); + return m_xRangeHighlighter; +} + +Reference<awt::XRequestCallback> SAL_CALL ChartModel::getPopupRequest() +{ + if (!m_xPopupRequest.is()) + m_xPopupRequest.set(new PopupRequest); + return m_xPopupRequest; +} + +rtl::Reference< ::chart::ChartTypeTemplate > ChartModel::impl_createDefaultChartTypeTemplate() +{ + rtl::Reference< ::chart::ChartTypeTemplate > xTemplate; + if( m_xChartTypeManager.is() ) + xTemplate = m_xChartTypeManager->createTemplate( "com.sun.star.chart2.template.Column" ); + return xTemplate; +} + +void SAL_CALL ChartModel::setChartTypeManager( const uno::Reference< chart2::XChartTypeManager >& xNewManager ) +{ + { + MutexGuard aGuard( m_aModelMutex ); + m_xChartTypeManager = dynamic_cast<::chart::ChartTypeManager*>(xNewManager.get()); + assert(!xNewManager || m_xChartTypeManager); + } + 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() +{ + return { datatransfer::DataFlavor( lcl_aGDIMetaFileMIMETypeHighContrast, + "GDIMetaFile", + cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) }; +} + +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 static_cast<cppu::OWeakObject*>(m_xXMLNamespaceMap.get()); + } + } + 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( comphelper::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() +{ + rtl::Reference< DataSource > xSource = DataSourceHelper::getUsedData( *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: */ |