diff options
Diffstat (limited to 'dbaccess/source/ui/app/subcomponentmanager.cxx')
-rw-r--r-- | dbaccess/source/ui/app/subcomponentmanager.cxx | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/dbaccess/source/ui/app/subcomponentmanager.cxx b/dbaccess/source/ui/app/subcomponentmanager.cxx new file mode 100644 index 000000000..08df1dff0 --- /dev/null +++ b/dbaccess/source/ui/app/subcomponentmanager.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 "subcomponentmanager.hxx" +#include "AppController.hxx" +#include <strings.hxx> + +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XModel2.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/embed/XComponentSupplier.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <tools/diagnose_ex.h> +#include <dbaccess/dataview.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> + +#include <algorithm> + +namespace dbaui +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::makeAny; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::frame::XFrame; + using ::com::sun::star::frame::XController; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::lang::XComponent; + using ::com::sun::star::frame::XModel2; + using ::com::sun::star::container::XEnumeration; + using ::com::sun::star::util::XCloseable; + using ::com::sun::star::awt::XTopWindow; + using ::com::sun::star::embed::XComponentSupplier; + using ::com::sun::star::ucb::XCommandProcessor; + using ::com::sun::star::ucb::Command; + using ::com::sun::star::document::XDocumentEventBroadcaster; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::PropertyChangeEvent; + + // helper structs + namespace + { + struct SubComponentDescriptor + { + /// the name of the sub component, empty if it is yet unsaved + OUString sName; + /// type of the component - an ElementType value, except for relation design + sal_Int32 nComponentType; + /// the mode in which the sub component has been opened + ElementOpenMode eOpenMode; + /// the frame which the component resides in. Must not be <NULL/> + Reference< XFrame > xFrame; + /// the controller of the sub component. Must not be <NULL/> + Reference< XController > xController; + /// the model of the sub component. Might be <NULL/> + Reference< XModel > xModel; + /// the document definition which holds the component, if any; as CommandProcessor + Reference< XCommandProcessor > xComponentCommandProcessor; + /// the document definition which holds the component, if any; as PropertySet + Reference< XPropertySet > xDocumentDefinitionProperties; + + SubComponentDescriptor() + :sName() + ,nComponentType( -1 ) + ,eOpenMode( E_OPEN_NORMAL ) + ,xFrame() + ,xController() + ,xModel() + { + } + + SubComponentDescriptor( const OUString& i_rName, const sal_Int32 i_nComponentType, + const ElementOpenMode i_eOpenMode, const Reference< XComponent >& i_rComponent ) + :sName( i_rName ) + ,nComponentType( i_nComponentType ) + ,eOpenMode( i_eOpenMode ) + { + if ( !impl_constructFrom( i_rComponent ) ) + { + // i_rComponent is neither a model, nor a controller, nor a frame + // => it must be a css.sdb.DocumentDefinition + Reference< XComponentSupplier > xCompSupp( i_rComponent, UNO_QUERY_THROW ); + Reference< XComponent > xComponent( xCompSupp->getComponent(), UNO_QUERY_THROW ); + if ( !impl_constructFrom( xComponent ) ) + throw RuntimeException("Illegal component type." ); + xComponentCommandProcessor.set( i_rComponent, UNO_QUERY_THROW ); + xDocumentDefinitionProperties.set( i_rComponent, UNO_QUERY_THROW ); + } + } + + bool is() const { return xFrame.is(); } + + private: + bool impl_constructFrom( const Reference< XComponent >& _rxComponent ) + { + // is it a model? + xModel.set( _rxComponent, UNO_QUERY ); + if ( xModel.is() ) + { + xController.set( xModel->getCurrentController() ); + if ( xController.is() ) + xFrame.set( xController->getFrame(), UNO_SET_THROW ); + } + else + { + // is it a controller? + xController.set( _rxComponent, UNO_QUERY ); + if ( xController.is() ) + { + xFrame.set( xController->getFrame(), UNO_SET_THROW ); + } + else + { + // is it a frame? + xFrame.set( _rxComponent, UNO_QUERY ); + if ( !xFrame.is() ) + return false; + + // ensure we have a controller + xController.set( xFrame->getController(), UNO_SET_THROW ); + } + + // check whether there is a model (not required) + xModel.set( xController->getModel() ); + } + + return true; + } + }; + + struct SelectSubComponent + { + Reference< XComponent > operator()( const SubComponentDescriptor &_desc ) const + { + if ( _desc.xModel.is() ) + return _desc.xModel.get(); + OSL_ENSURE( _desc.xController.is(), "SelectSubComponent::operator(): illegal component!" ); + return _desc.xController.get(); + } + }; + + typedef std::vector< SubComponentDescriptor > SubComponents; + + struct SubComponentMatch + { + public: + SubComponentMatch( const OUString& i_rName, const sal_Int32 i_nComponentType, + const ElementOpenMode i_eOpenMode ) + :m_sName( i_rName ) + ,m_nComponentType( i_nComponentType ) + ,m_eOpenMode( i_eOpenMode ) + { + } + + bool operator()( const SubComponentDescriptor& i_rCompareWith ) const + { + return ( m_sName == i_rCompareWith.sName ) + && ( m_nComponentType == i_rCompareWith.nComponentType ) + && ( m_eOpenMode == i_rCompareWith.eOpenMode ); + } + private: + const OUString m_sName; + const sal_Int32 m_nComponentType; + const ElementOpenMode m_eOpenMode; + }; + } + + // SubComponentManager_Data + struct SubComponentManager_Data + { + SubComponentManager_Data( OApplicationController& _rController, const ::comphelper::SharedMutex& _rMutex ) + :m_rController( _rController ) + ,m_aMutex( _rMutex ) + { + } + + OApplicationController& m_rController; + mutable ::comphelper::SharedMutex m_aMutex; + SubComponents m_aComponents; + + ::osl::Mutex& getMutex() const { return m_aMutex; } + }; + + // SubComponentManager + SubComponentManager::SubComponentManager( OApplicationController& _rController, const ::comphelper::SharedMutex& _rMutex ) + :m_pData( new SubComponentManager_Data( _rController, _rMutex ) ) + { + } + + SubComponentManager::~SubComponentManager() + { + } + + void SubComponentManager::disposing() + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + m_pData->m_aComponents.clear(); + } + + namespace + { + bool lcl_fallbackToAnotherController( SubComponentDescriptor& _rCompDesc ) + { + Reference< XController > xFallback; + OSL_PRECOND( _rCompDesc.xModel.is(), "lcl_fallbackToAnotherController: illegal call!" ); + if ( !_rCompDesc.xModel.is() ) + return false; + + xFallback.set( _rCompDesc.xModel->getCurrentController() ); + if ( xFallback == _rCompDesc.xController ) + // don't accept the very same controller as fallback + xFallback.clear(); + + if ( !xFallback.is() ) + { + // perhaps XModel2 can be of help here + Reference< XModel2 > xModel2( _rCompDesc.xModel, UNO_QUERY ); + Reference< XEnumeration > xControllerEnum; + if ( xModel2.is() ) + xControllerEnum = xModel2->getControllers(); + while ( xControllerEnum.is() && xControllerEnum->hasMoreElements() ) + { + xFallback.set( xControllerEnum->nextElement(), UNO_QUERY ); + if ( xFallback == _rCompDesc.xController ) + xFallback.clear(); + } + } + + if ( xFallback.is() ) + { + _rCompDesc.xController = xFallback; + _rCompDesc.xFrame.set( xFallback->getFrame(), UNO_SET_THROW ); + return true; + } + + return false; + } + + bool lcl_closeComponent( const Reference< XCommandProcessor >& _rxCommandProcessor ) + { + bool bSuccess = false; + try + { + sal_Int32 nCommandIdentifier = _rxCommandProcessor->createCommandIdentifier(); + + Command aCommand; + aCommand.Name = "close"; + _rxCommandProcessor->execute( aCommand, nCommandIdentifier, nullptr ); + bSuccess = true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return bSuccess; + } + + bool lcl_closeComponent( const SubComponentDescriptor& _rComponent ) + { + if ( _rComponent.xComponentCommandProcessor.is() ) + return lcl_closeComponent( _rComponent.xComponentCommandProcessor ); + + Reference< XController > xController( _rComponent.xController ); + OSL_ENSURE( xController.is(), "lcl_closeComponent: invalid controller!" ); + + // suspend the controller in the document + if ( xController.is() ) + if ( !xController->suspend( true ) ) + return false; + + bool bSuccess = false; + try + { + Reference< XCloseable > xCloseable( _rComponent.xFrame, UNO_QUERY_THROW ); + xCloseable->close( true ); + bSuccess = true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return bSuccess; + } + + void lcl_notifySubComponentEvent( const SubComponentManager_Data& _rData, const char* _pAsciiEventName, + const SubComponentDescriptor& _rComponent ) + { + try + { + Reference< XDocumentEventBroadcaster > xBroadcaster( _rData.m_rController.getModel(), UNO_QUERY_THROW ); + xBroadcaster->notifyDocumentEvent( + OUString::createFromAscii( _pAsciiEventName ), + &_rData.m_rController, + makeAny( _rComponent.xFrame ) + ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + void SAL_CALL SubComponentManager::propertyChange( const PropertyChangeEvent& i_rEvent ) + { + if ( i_rEvent.PropertyName != PROPERTY_NAME ) + // by definition, it's allowed to broadcast more than what we've registered for + return; + + // find the sub component whose name changed + for (auto & component : m_pData->m_aComponents) + { + if ( component.xDocumentDefinitionProperties != i_rEvent.Source ) + continue; + + OUString sNewName; + OSL_VERIFY( i_rEvent.NewValue >>= sNewName ); + + #if OSL_DEBUG_LEVEL > 0 + OUString sOldKnownName( component.sName ); + OUString sOldName; + OSL_VERIFY( i_rEvent.OldValue >>= sOldName ); + OSL_ENSURE( sOldName == sOldKnownName, "SubComponentManager::propertyChange: inconsistency in the old names!" ); + #endif + + component.sName = sNewName; + break; + } + } + + void SAL_CALL SubComponentManager::disposing( const EventObject& _rSource ) + { + ::osl::ClearableMutexGuard aGuard( m_pData->getMutex() ); + + SubComponentDescriptor aClosedComponent; + + for ( SubComponents::iterator comp = m_pData->m_aComponents.begin(); + comp != m_pData->m_aComponents.end(); + ++comp + ) + { + bool bRemove = false; + + if ( comp->xController == _rSource.Source ) + { + if ( !comp->xModel.is() ) + { + bRemove = true; + } + else + { + // maybe this is just one view to the sub document, and only this view is closed + if ( !lcl_fallbackToAnotherController( *comp ) ) + { + bRemove = true; + } + } + } + else if ( comp->xModel == _rSource.Source ) + { + bRemove = true; + } + + if ( bRemove ) + { + aClosedComponent = *comp; + m_pData->m_aComponents.erase( comp ); + break; + } + } + + if ( aClosedComponent.is() ) + { + aGuard.clear(); + lcl_notifySubComponentEvent( *m_pData, "OnSubComponentClosed", aClosedComponent ); + } + } + + Sequence< Reference< XComponent> > SubComponentManager::getSubComponents() const + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + + Sequence< Reference< XComponent > > aComponents( m_pData->m_aComponents.size() ); + std::transform( + m_pData->m_aComponents.begin(), + m_pData->m_aComponents.end(), + aComponents.getArray(), + SelectSubComponent() + ); + return aComponents; + } + + bool SubComponentManager::closeSubComponents() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + + try + { + SubComponents aWorkingCopy( m_pData->m_aComponents ); + for (auto const& elem : aWorkingCopy) + { + lcl_closeComponent(elem); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return empty(); + } + + bool SubComponentManager::empty() const + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + return m_pData->m_aComponents.empty(); + } + + void SubComponentManager::onSubComponentOpened( const OUString& _rName, const sal_Int32 _nComponentType, + const ElementOpenMode _eOpenMode, const Reference< XComponent >& _rxComponent ) + { + ::osl::ClearableMutexGuard aGuard( m_pData->getMutex() ); + +#if OSL_DEBUG_LEVEL > 0 + if ( !_rName.isEmpty() ) + { + // check there does not already exist such a component + auto subComponentNotExists = std::none_of( + m_pData->m_aComponents.begin(), + m_pData->m_aComponents.end(), + SubComponentMatch( _rName, _nComponentType, _eOpenMode ) + ); + OSL_ENSURE( subComponentNotExists, "already existent!" ); + } +#endif + SubComponentDescriptor aElement( _rName, _nComponentType, _eOpenMode, _rxComponent ); + ENSURE_OR_THROW( aElement.xModel.is() || aElement.xController.is(), "illegal component" ); + + m_pData->m_aComponents.push_back( aElement ); + + // add as listener + if ( aElement.xController.is() ) + aElement.xController->addEventListener( this ); + if ( aElement.xModel.is() ) + aElement.xModel->addEventListener( this ); + if ( aElement.xDocumentDefinitionProperties.is() ) + aElement.xDocumentDefinitionProperties->addPropertyChangeListener( PROPERTY_NAME, this ); + + // notify this to interested parties + aGuard.clear(); + lcl_notifySubComponentEvent( *m_pData, "OnSubComponentOpened", aElement ); + } + + bool SubComponentManager::activateSubFrame( const OUString& _rName, const sal_Int32 _nComponentType, + const ElementOpenMode _eOpenMode, Reference< XComponent >& o_rComponent ) const + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + + SubComponents::const_iterator pos = std::find_if( + m_pData->m_aComponents.begin(), + m_pData->m_aComponents.end(), + SubComponentMatch( _rName, _nComponentType, _eOpenMode ) + ); + if ( pos == m_pData->m_aComponents.end() ) + // no component with this name/type/open mode + return false; + + const Reference< XFrame > xFrame( pos->xFrame, UNO_SET_THROW ); + const Reference< XTopWindow > xTopWindow( xFrame->getContainerWindow(), UNO_QUERY_THROW ); + xTopWindow->toFront(); + + if ( pos->xModel.is() ) + o_rComponent = pos->xModel.get(); + else if ( pos->xController.is() ) + o_rComponent = pos->xController.get(); + else + o_rComponent = pos->xFrame.get(); + + return true; + } + + bool SubComponentManager::closeSubFrames( const OUString& i_rName, const sal_Int32 _nComponentType ) + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + ENSURE_OR_RETURN_FALSE( !i_rName.isEmpty(), "SubComponentManager::closeSubFrames: illegal name!" ); + + SubComponents aWorkingCopy( m_pData->m_aComponents ); + for (auto const& elem : aWorkingCopy) + { + if ( ( elem.sName != i_rName ) || ( elem.nComponentType != _nComponentType ) ) + continue; + + if ( !lcl_closeComponent(elem) ) + return false; + } + + return true; + } + + bool SubComponentManager::lookupSubComponent( const Reference< XComponent >& i_rComponent, + OUString& o_rName, sal_Int32& o_rComponentType ) + { + for (auto const& component : m_pData->m_aComponents) + { + if ( ( component.xModel.is() + && ( component.xModel == i_rComponent ) + ) + || ( component.xController.is() + && ( component.xController == i_rComponent ) + ) + || ( component.xFrame.is() + && ( component.xFrame == i_rComponent ) + ) + ) + { + o_rName = component.sName; + o_rComponentType = component.nComponentType; + return true; + } + } + return false; + } + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |