552 lines
20 KiB
C++
552 lines
20 KiB
C++
/* -*- 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 <comphelper/diagnose_ex.hxx>
|
|
#include <dbaccess/dataview.hxx>
|
|
#include <utility>
|
|
#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::Any;
|
|
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()
|
|
:nComponentType( -1 )
|
|
,eOpenMode( ElementOpenMode::Normal )
|
|
{
|
|
}
|
|
|
|
SubComponentDescriptor( OUString i_sName, const sal_Int32 i_nComponentType,
|
|
const ElementOpenMode i_eOpenMode, const Reference< XComponent >& i_rComponent )
|
|
:sName(std::move( i_sName ))
|
|
,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(u"Illegal component type."_ustr );
|
|
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;
|
|
OSL_ENSURE( _desc.xController.is(), "SelectSubComponent::operator(): illegal component!" );
|
|
return _desc.xController;
|
|
}
|
|
};
|
|
|
|
typedef std::vector< SubComponentDescriptor > SubComponents;
|
|
|
|
struct SubComponentMatch
|
|
{
|
|
public:
|
|
SubComponentMatch( OUString i_sName, const sal_Int32 i_nComponentType,
|
|
const ElementOpenMode i_eOpenMode )
|
|
:m_sName(std::move( i_sName ))
|
|
,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, ::comphelper::SharedMutex _aMutex )
|
|
:m_rController( _rController )
|
|
,m_aMutex(std::move( _aMutex ))
|
|
{
|
|
}
|
|
|
|
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 OUString& _rAsciiEventName,
|
|
const SubComponentDescriptor& _rComponent )
|
|
{
|
|
try
|
|
{
|
|
Reference< XDocumentEventBroadcaster > xBroadcaster( _rData.m_rController.getModel(), UNO_QUERY_THROW );
|
|
xBroadcaster->notifyDocumentEvent(
|
|
_rAsciiEventName,
|
|
&_rData.m_rController,
|
|
Any( _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, u"OnSubComponentClosed"_ustr, 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, u"OnSubComponentOpened"_ustr, 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( std::u16string_view i_rName, const sal_Int32 _nComponentType )
|
|
{
|
|
::osl::MutexGuard aGuard( m_pData->getMutex() );
|
|
ENSURE_OR_RETURN_FALSE( !i_rName.empty(), "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: */
|