/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 Reference< XFrame > xFrame; /// the controller of the sub component. Must not be Reference< XController > xController; /// the model of the sub component. Might be 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: */