blob: e492d2102935f604f9199a6f13676b8e08d12b5c [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "subcomponentmanager.hxx"
#include "AppController.hxx"
#include "dbustrings.hrc"
/** === begin UNO includes === **/
#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>
/** === end UNO includes === **/
#include <tools/diagnose_ex.h>
#include <vcl/svapp.hxx>
#include <vos/mutex.hxx>
#include <hash_map>
#include <algorithm>
#include <functional>
//......................................................................................................................
namespace dbaui
{
//......................................................................................................................
/** === begin UNO using === **/
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::XInterface;
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::Any;
using ::com::sun::star::uno::makeAny;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Type;
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;
/** === end UNO using === **/
//==================================================================================================================
//= helper structs
//==================================================================================================================
namespace
{
//..............................................................................................................
struct SubComponentDescriptor
{
/// the name of the sub component, empty if it is yet unsaved
::rtl::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 ::rtl::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( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Illegal component type." ) ), NULL );
xComponentCommandProcessor.set( i_rComponent, UNO_QUERY_THROW );
xDocumentDefinitionProperties.set( i_rComponent, UNO_QUERY_THROW );
}
}
inline 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 wether there is a model (not required)
xModel.set( xController->getModel() );
}
return true;
}
};
//..............................................................................................................
struct SelectSubComponent : public ::std::unary_function< SubComponentDescriptor, Reference< XComponent > >
{
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 ::std::unary_function< SubComponentDescriptor, bool >
{
public:
SubComponentMatch( const ::rtl::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 ::rtl::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
{
Reference< XCommandProcessor > xCommandProcessor( _rxCommandProcessor, UNO_SET_THROW );
sal_Int32 nCommandIdentifier = xCommandProcessor->createCommandIdentifier();
Command aCommand;
aCommand.Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "close" ) );
xCommandProcessor->execute( aCommand, nCommandIdentifier, NULL );
bSuccess = true;
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION();
}
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( sal_True ) )
return false;
bool bSuccess = false;
try
{
Reference< XCloseable > xCloseable( _rComponent.xFrame, UNO_QUERY_THROW );
xCloseable->close( sal_True );
bSuccess = true;
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION();
}
return bSuccess;
}
//..............................................................................................................
void lcl_notifySubComponentEvent( const SubComponentManager_Data& _rData, const sal_Char* _pAsciiEventName,
const SubComponentDescriptor& _rComponent )
{
try
{
Reference< XDocumentEventBroadcaster > xBroadcaster( _rData.m_rController.getModel(), UNO_QUERY_THROW );
xBroadcaster->notifyDocumentEvent(
::rtl::OUString::createFromAscii( _pAsciiEventName ),
&_rData.m_rController,
makeAny( _rComponent.xFrame )
);
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION();
}
}
}
//------------------------------------------------------------------------------------------------------------------
void SAL_CALL SubComponentManager::propertyChange( const PropertyChangeEvent& i_rEvent ) throw (RuntimeException)
{
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 ( SubComponents::iterator comp = m_pData->m_aComponents.begin();
comp != m_pData->m_aComponents.end();
++comp
)
{
if ( comp->xDocumentDefinitionProperties != i_rEvent.Source )
continue;
::rtl::OUString sNewName;
OSL_VERIFY( i_rEvent.NewValue >>= sNewName );
#if OSL_DEBUG_LEVEL > 0
::rtl::OUString sOldKnownName( comp->sName );
::rtl::OUString sOldName;
OSL_VERIFY( i_rEvent.OldValue >>= sOldName );
OSL_ENSURE( sOldName == sOldKnownName, "SubComponentManager::propertyChange: inconsistency in the old names!" );
#endif
comp->sName = sNewName;
break;
}
}
//------------------------------------------------------------------------------------------------------------------
void SAL_CALL SubComponentManager::disposing( const EventObject& _rSource ) throw (RuntimeException)
{
::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;
}
//------------------------------------------------------------------------------------------------------------------
sal_Bool SubComponentManager::closeSubComponents()
{
::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
::osl::MutexGuard aGuard( m_pData->getMutex() );
try
{
SubComponents aWorkingCopy( m_pData->m_aComponents );
for ( SubComponents::const_iterator comp = aWorkingCopy.begin();
comp != aWorkingCopy.end();
++comp
)
{
lcl_closeComponent( *comp );
}
}
catch ( const Exception& )
{
DBG_UNHANDLED_EXCEPTION();
}
return empty();
}
//------------------------------------------------------------------------------------------------------------------
bool SubComponentManager::empty() const
{
::osl::MutexGuard aGuard( m_pData->getMutex() );
return m_pData->m_aComponents.empty();
}
//------------------------------------------------------------------------------------------------------------------
void SubComponentManager::onSubComponentOpened( const ::rtl::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.getLength() )
{
// check there does not already exist such a component
SubComponents::const_iterator existentPos = ::std::find_if(
m_pData->m_aComponents.begin(),
m_pData->m_aComponents.end(),
SubComponentMatch( _rName, _nComponentType, _eOpenMode )
);
OSL_ENSURE( existentPos == m_pData->m_aComponents.end(), "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 ::rtl::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 ::rtl::OUString& i_rName, const sal_Int32 _nComponentType )
{
::osl::MutexGuard aGuard( m_pData->getMutex() );
ENSURE_OR_RETURN_FALSE( i_rName.getLength(), "SubComponentManager::closeSubFrames: illegal name!" );
SubComponents aWorkingCopy( m_pData->m_aComponents );
for ( SubComponents::const_iterator comp = aWorkingCopy.begin();
comp != aWorkingCopy.end();
++comp
)
{
if ( ( comp->sName != i_rName ) || ( comp->nComponentType != _nComponentType ) )
continue;
if ( !lcl_closeComponent( *comp ) )
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------
bool SubComponentManager::lookupSubComponent( const Reference< XComponent >& i_rComponent,
::rtl::OUString& o_rName, sal_Int32& o_rComponentType )
{
for ( SubComponents::const_iterator comp = m_pData->m_aComponents.begin();
comp != m_pData->m_aComponents.end();
++comp
)
{
if ( ( comp->xModel.is()
&& ( comp->xModel == i_rComponent )
)
|| ( comp->xController.is()
&& ( comp->xController == i_rComponent )
)
|| ( comp->xFrame.is()
&& ( comp->xFrame == i_rComponent )
)
)
{
o_rName = comp->sName;
o_rComponentType = comp->nComponentType;
return true;
}
}
return false;
}
//......................................................................................................................
} // namespace dbaui
//......................................................................................................................