blob: ef154ab3f8eba00ac63453cff33c8f1a78fa3efa [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 "vbaeventshelper.hxx"
#include <com/sun/star/awt/XTopWindow.hpp>
#include <com/sun/star/awt/XTopWindowListener.hpp>
#include <com/sun/star/awt/XWindowListener.hpp>
#include <com/sun/star/frame/XBorderResizeListener.hpp>
#include <com/sun/star/frame/XControllerBorder.hpp>
#include <com/sun/star/script/ModuleType.hpp>
#include <com/sun/star/script/vba/VBAEventId.hpp>
#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
#include <com/sun/star/table/XCellRange.hpp>
#include <com/sun/star/util/XChangesListener.hpp>
#include <com/sun/star/util/XChangesNotifier.hpp>
#include <cppuhelper/implbase4.hxx>
#include <toolkit/unohlp.hxx>
#include <unotools/eventcfg.hxx>
#include <vbahelper/helperdecl.hxx>
#include <vcl/svapp.hxx>
#include <vcl/window.hxx>
#include "cellsuno.hxx"
#include "convuno.hxx"
#include "vbaapplication.hxx"
using namespace ::com::sun::star;
using namespace ::com::sun::star::script::vba::VBAEventId;
using namespace ::ooo::vba;
using ::rtl::OUString;
// ============================================================================
namespace {
/** Extracts a sheet index from the specified element of the passed sequence.
The element may be an integer, a Calc range or ranges object, or a VBA Range object. */
SCTAB lclGetTabFromArgs( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) throw (lang::IllegalArgumentException)
{
VbaEventsHelperBase::checkArgument( rArgs, nIndex );
// first try to extract a sheet index
sal_Int32 nTab = -1;
if( rArgs[ nIndex ] >>= nTab )
{
if( (nTab < 0) || (nTab > MAXTAB) )
throw lang::IllegalArgumentException();
return static_cast< SCTAB >( nTab );
}
// try VBA Range object
uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
if( xVbaRange.is() )
{
uno::Reference< XHelperInterface > xVbaHelper( xVbaRange, uno::UNO_QUERY_THROW );
// TODO: in the future, the parent may be an excel::XChart (chart sheet) -> will there be a common base interface?
uno::Reference< excel::XWorksheet > xVbaSheet( xVbaHelper->getParent(), uno::UNO_QUERY_THROW );
// VBA sheet index is 1-based
return static_cast< SCTAB >( xVbaSheet->getIndex() - 1 );
}
// try single UNO range object
uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable = getXSomethingFromArgs< sheet::XCellRangeAddressable >( rArgs, nIndex );
if( xCellRangeAddressable.is() )
return xCellRangeAddressable->getRangeAddress().Sheet;
// at last, try UNO range list
uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
if( xRanges.is() )
{
uno::Sequence< table::CellRangeAddress > aRangeAddresses = xRanges->getRangeAddresses();
if( aRangeAddresses.getLength() > 0 )
return aRangeAddresses[ 0 ].Sheet;
}
throw lang::IllegalArgumentException();
}
/** Returns the AWT container window of the passed controller. */
uno::Reference< awt::XWindow > lclGetWindowForController( const uno::Reference< frame::XController >& rxController )
{
if( rxController.is() ) try
{
uno::Reference< frame::XFrame > xFrame( rxController->getFrame(), uno::UNO_SET_THROW );
return xFrame->getContainerWindow();
}
catch( uno::Exception& )
{
}
return 0;
}
} // namespace
// ============================================================================
typedef ::cppu::WeakImplHelper4< awt::XTopWindowListener, awt::XWindowListener, frame::XBorderResizeListener, util::XChangesListener > ScVbaEventListener_BASE;
// This class is to process Workbook window related event
class ScVbaEventListener : public ScVbaEventListener_BASE
{
public :
ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell );
virtual ~ScVbaEventListener();
/** Starts listening to the passed document controller. */
void startControllerListening( const uno::Reference< frame::XController >& rxController );
/** Stops listening to the passed document controller. */
void stopControllerListening( const uno::Reference< frame::XController >& rxController );
// XTopWindowListener
virtual void SAL_CALL windowOpened( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowClosing( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowClosed( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowMinimized( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowNormalized( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowActivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowDeactivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
// XWindowListener
virtual void SAL_CALL windowResized( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowMoved( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowShown( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
virtual void SAL_CALL windowHidden( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
// XBorderResizeListener
virtual void SAL_CALL borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& aNewSize ) throw (uno::RuntimeException);
// XChangesListener
virtual void SAL_CALL changesOccurred( const util::ChangesEvent& rEvent ) throw (uno::RuntimeException);
// XEventListener
virtual void SAL_CALL disposing( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
private:
/** Starts listening to the document model. */
void startModelListening();
/** Stops listening to the document model. */
void stopModelListening();
/** Returns the controller for the passed VCL window. */
uno::Reference< frame::XController > getControllerForWindow( Window* pWindow ) const;
/** Calls the Workbook_Window[Activate|Deactivate] event handler. */
void processWindowActivateEvent( Window* pWindow, bool bActivate );
/** Posts a Workbook_WindowResize user event. */
void postWindowResizeEvent( Window* pWindow );
/** Callback link for Application::PostUserEvent(). */
DECL_LINK( processWindowResizeEvent, Window* );
private:
typedef ::std::map< Window*, uno::Reference< frame::XController > > WindowControllerMap;
::osl::Mutex maMutex;
ScVbaEventsHelper& mrVbaEvents;
uno::Reference< frame::XModel > mxModel;
ScDocShell* mpDocShell;
WindowControllerMap maControllers; /// Maps VCL top windows to their controllers.
Window* mpActiveWindow; /// Currently activated window, to prevent multiple (de)activation.
bool mbWindowResized; /// True = window resize system event processed.
bool mbBorderChanged; /// True = borders changed system event processed.
bool mbDisposed;
};
// ----------------------------------------------------------------------------
ScVbaEventListener::ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell ) :
mrVbaEvents( rVbaEvents ),
mxModel( rxModel ),
mpDocShell( pDocShell ),
mpActiveWindow( 0 ),
mbWindowResized( false ),
mbBorderChanged( false ),
mbDisposed( !rxModel.is() )
{
if( !mxModel.is() )
return;
startModelListening();
try
{
uno::Reference< frame::XController > xController( mxModel->getCurrentController(), uno::UNO_QUERY_THROW );
startControllerListening( xController );
}
catch( uno::Exception& )
{
}
}
ScVbaEventListener::~ScVbaEventListener()
{
}
void ScVbaEventListener::startControllerListening( const uno::Reference< frame::XController >& rxController )
{
::osl::MutexGuard aGuard( maMutex );
uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
if( xWindow.is() )
try { xWindow->addWindowListener( this ); } catch( uno::Exception& ) {}
uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
if( xTopWindow.is() )
try { xTopWindow->addTopWindowListener( this ); } catch( uno::Exception& ) {}
uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
if( xControllerBorder.is() )
try { xControllerBorder->addBorderResizeListener( this ); } catch( uno::Exception& ) {}
if( Window* pWindow = VCLUnoHelper::GetWindow( xWindow ) )
maControllers[ pWindow ] = rxController;
}
void ScVbaEventListener::stopControllerListening( const uno::Reference< frame::XController >& rxController )
{
::osl::MutexGuard aGuard( maMutex );
uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
if( xWindow.is() )
try { xWindow->removeWindowListener( this ); } catch( uno::Exception& ) {}
uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
if( xTopWindow.is() )
try { xTopWindow->removeTopWindowListener( this ); } catch( uno::Exception& ) {}
uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
if( xControllerBorder.is() )
try { xControllerBorder->removeBorderResizeListener( this ); } catch( uno::Exception& ) {}
if( Window* pWindow = VCLUnoHelper::GetWindow( xWindow ) )
{
maControllers.erase( pWindow );
if( pWindow == mpActiveWindow )
mpActiveWindow = 0;
}
}
void SAL_CALL ScVbaEventListener::windowOpened( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::windowClosing( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::windowClosed( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::windowMinimized( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::windowNormalized( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::windowActivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
{
::osl::MutexGuard aGuard( maMutex );
if( !mbDisposed )
{
uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
Window* pWindow = VCLUnoHelper::GetWindow( xWindow );
OSL_TRACE( "ScVbaEventListener::windowActivated - pWindow = 0x%x, mpActiveWindow = 0x%x", pWindow, mpActiveWindow );
// do not fire activation event multiple time for the same window
if( pWindow && (pWindow != mpActiveWindow) )
{
// if another window is active, fire deactivation event first
if( mpActiveWindow )
processWindowActivateEvent( mpActiveWindow, false );
// fire activation event for the new window
processWindowActivateEvent( pWindow, true );
mpActiveWindow = pWindow;
}
}
}
void SAL_CALL ScVbaEventListener::windowDeactivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
{
::osl::MutexGuard aGuard( maMutex );
if( !mbDisposed )
{
uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
Window* pWindow = VCLUnoHelper::GetWindow( xWindow );
OSL_TRACE( "ScVbaEventListener::windowDeactivated - pWindow = 0x%x, mpActiveWindow = 0x%x", pWindow, mpActiveWindow );
// do not fire the deactivation event, if the window is not active (prevent multiple deactivation)
if( pWindow && (pWindow == mpActiveWindow) )
processWindowActivateEvent( pWindow, false );
// forget pointer to the active window
mpActiveWindow = 0;
}
}
void SAL_CALL ScVbaEventListener::windowResized( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException)
{
::osl::MutexGuard aGuard( maMutex );
mbWindowResized = true;
if( !mbDisposed && mbBorderChanged )
{
uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
}
}
void SAL_CALL ScVbaEventListener::windowMoved( const awt::WindowEvent& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::windowShown( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::windowHidden( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
{
}
void SAL_CALL ScVbaEventListener::borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& /*aNewSize*/ ) throw (uno::RuntimeException)
{
::osl::MutexGuard aGuard( maMutex );
mbBorderChanged = true;
if( !mbDisposed && mbWindowResized )
{
uno::Reference< frame::XController > xController( rSource, uno::UNO_QUERY );
uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( xController );
postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
}
}
void SAL_CALL ScVbaEventListener::changesOccurred( const util::ChangesEvent& rEvent ) throw (uno::RuntimeException)
{
::osl::MutexGuard aGuard( maMutex );
sal_Int32 nCount = rEvent.Changes.getLength();
if( mbDisposed || !mpDocShell || (nCount == 0) )
return;
util::ElementChange aChange = rEvent.Changes[ 0 ];
OUString sOperation;
aChange.Accessor >>= sOperation;
if( !sOperation.equalsIgnoreAsciiCaseAscii("cell-change") )
return;
if( nCount == 1 )
{
uno::Reference< table::XCellRange > xRangeObj;
aChange.ReplacedElement >>= xRangeObj;
if( xRangeObj.is() )
{
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[0] <<= xRangeObj;
mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
}
return;
}
ScRangeList aRangeList;
for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
{
aChange = rEvent.Changes[ nIndex ];
aChange.Accessor >>= sOperation;
uno::Reference< table::XCellRange > xRangeObj;
aChange.ReplacedElement >>= xRangeObj;
if( xRangeObj.is() && sOperation.equalsIgnoreAsciiCaseAscii("cell-change") )
{
uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable( xRangeObj, uno::UNO_QUERY );
if( xCellRangeAddressable.is() )
{
ScRange aRange;
ScUnoConversion::FillScRange( aRange, xCellRangeAddressable->getRangeAddress() );
aRangeList.Append( aRange );
}
}
}
if( aRangeList.Count() > 0 )
{
uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( mpDocShell, aRangeList ) );
uno::Sequence< uno::Any > aArgs(1);
aArgs[0] <<= xRanges;
mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
}
}
void SAL_CALL ScVbaEventListener::disposing( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
{
::osl::MutexGuard aGuard( maMutex );
uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
if( xModel.is() )
{
OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
stopModelListening();
mbDisposed = true;
return;
}
uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
if( xController.is() )
{
stopControllerListening( xController );
return;
}
}
// private --------------------------------------------------------------------
void ScVbaEventListener::startModelListening()
{
try
{
uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
xChangesNotifier->addChangesListener( this );
}
catch( uno::Exception& )
{
}
}
void ScVbaEventListener::stopModelListening()
{
try
{
uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
xChangesNotifier->removeChangesListener( this );
}
catch( uno::Exception& )
{
}
}
uno::Reference< frame::XController > ScVbaEventListener::getControllerForWindow( Window* pWindow ) const
{
WindowControllerMap::const_iterator aIt = maControllers.find( pWindow );
return (aIt == maControllers.end()) ? uno::Reference< frame::XController >() : aIt->second;
}
void ScVbaEventListener::processWindowActivateEvent( Window* pWindow, bool bActivate )
{
uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
if( xController.is() )
{
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[ 0 ] <<= xController;
mrVbaEvents.processVbaEventNoThrow( bActivate ? WORKBOOK_WINDOWACTIVATE : WORKBOOK_WINDOWDEACTIVATE, aArgs );
}
}
void ScVbaEventListener::postWindowResizeEvent( Window* pWindow )
{
// check that the passed window is still alive (it must be registered in maControllers)
if( pWindow && (maControllers.count( pWindow ) > 0) )
{
mbWindowResized = mbBorderChanged = false;
acquire(); // ensure we don't get deleted before the timer fires
Application::PostUserEvent( LINK( this, ScVbaEventListener, processWindowResizeEvent ), pWindow );
}
}
IMPL_LINK( ScVbaEventListener, processWindowResizeEvent, Window*, EMPTYARG pWindow )
{
::osl::MutexGuard aGuard( maMutex );
/* Check that the passed window is still alive (it must be registered in
maControllers). While closing a document, postWindowResizeEvent() may
be called on the last window which posts a user event via
Application::PostUserEvent to call this event handler. VCL will trigger
the handler some time later. Sometimes, the window gets deleted before.
This is handled via the disposing() function which removes the window
pointer from the member maControllers. Thus, checking whether
maControllers contains pWindow ensures that the window is still alive. */
if( !mbDisposed && pWindow && (maControllers.count( pWindow ) > 0) )
{
// do not fire event unless all mouse buttons have been released
Window::PointerState aPointerState = pWindow->GetPointerState();
if( (aPointerState.mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0 )
{
uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
if( xController.is() )
{
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[ 0 ] <<= xController;
// #163419# do not throw exceptions into application core
mrVbaEvents.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE, aArgs );
}
}
}
release();
return 0;
}
// ============================================================================
ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs, const uno::Reference< uno::XComponentContext >& xContext ) :
VbaEventsHelperBase( rArgs, xContext ),
mbOpened( false )
{
mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
mpDoc = mpDocShell ? mpDocShell->GetDocument() : 0;
if( !mxModel.is() || !mpDocShell || !mpDoc )
return;
#define REGISTER_EVENT( eventid, moduletype, classname, eventname, cancelindex, worksheet ) \
registerEventHandler( eventid, moduletype, classname "_" eventname, cancelindex, uno::Any( worksheet ) )
#define REGISTER_AUTO_EVENT( eventid, eventname ) \
REGISTER_EVENT( AUTO_##eventid, script::ModuleType::NORMAL, "Auto", eventname, -1, false )
#define REGISTER_WORKBOOK_EVENT( eventid, eventname, cancelindex ) \
REGISTER_EVENT( WORKBOOK_##eventid, script::ModuleType::DOCUMENT, "Workbook", eventname, cancelindex, false )
#define REGISTER_WORKSHEET_EVENT( eventid, eventname, cancelindex ) \
REGISTER_EVENT( WORKSHEET_##eventid, script::ModuleType::DOCUMENT, "Worksheet", eventname, cancelindex, true ); \
REGISTER_EVENT( (USERDEFINED_START + WORKSHEET_##eventid), script::ModuleType::DOCUMENT, "Workbook", "Sheet" eventname, (((cancelindex) >= 0) ? ((cancelindex) + 1) : -1), false )
// global
REGISTER_AUTO_EVENT( OPEN, "Open" );
REGISTER_AUTO_EVENT( CLOSE, "Close" );
// Workbook
REGISTER_WORKBOOK_EVENT( ACTIVATE, "Activate", -1 );
REGISTER_WORKBOOK_EVENT( DEACTIVATE, "Deactivate", -1 );
REGISTER_WORKBOOK_EVENT( OPEN, "Open", -1 );
REGISTER_WORKBOOK_EVENT( BEFORECLOSE, "BeforeClose", 0 );
REGISTER_WORKBOOK_EVENT( BEFOREPRINT, "BeforePrint", 0 );
REGISTER_WORKBOOK_EVENT( BEFORESAVE, "BeforeSave", 1 );
REGISTER_WORKBOOK_EVENT( AFTERSAVE, "AfterSave", -1 );
REGISTER_WORKBOOK_EVENT( NEWSHEET, "NewSheet", -1 );
REGISTER_WORKBOOK_EVENT( WINDOWACTIVATE, "WindowActivate", -1 );
REGISTER_WORKBOOK_EVENT( WINDOWDEACTIVATE, "WindowDeactivate", -1 );
REGISTER_WORKBOOK_EVENT( WINDOWRESIZE, "WindowResize", -1 );
// Worksheet events. All events have a corresponding workbook event.
REGISTER_WORKSHEET_EVENT( ACTIVATE, "Activate", -1 );
REGISTER_WORKSHEET_EVENT( DEACTIVATE, "Deactivate", -1 );
REGISTER_WORKSHEET_EVENT( BEFOREDOUBLECLICK, "BeforeDoubleClick", 1 );
REGISTER_WORKSHEET_EVENT( BEFORERIGHTCLICK, "BeforeRightClick", 1 );
REGISTER_WORKSHEET_EVENT( CALCULATE, "Calculate", -1 );
REGISTER_WORKSHEET_EVENT( CHANGE, "Change", -1 );
REGISTER_WORKSHEET_EVENT( SELECTIONCHANGE, "SelectionChange", -1 );
REGISTER_WORKSHEET_EVENT( FOLLOWHYPERLINK, "FollowHyperlink", -1 );
#undef REGISTER_WORKSHEET_EVENT
#undef REGISTER_WORKBOOK_EVENT
#undef REGISTER_AUTO_EVENT
#undef REGISTER_EVENT
}
ScVbaEventsHelper::~ScVbaEventsHelper()
{
}
void SAL_CALL ScVbaEventsHelper::notifyEvent( const css::document::EventObject& rEvent ) throw (css::uno::RuntimeException)
{
static const uno::Sequence< uno::Any > saEmptyArgs;
if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_OPENDOC )) ||
(rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CREATEDOC )) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
{
processVbaEventNoThrow( WORKBOOK_OPEN, saEmptyArgs );
}
else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_ACTIVATEDOC ) )
{
processVbaEventNoThrow( WORKBOOK_ACTIVATE, saEmptyArgs );
}
else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_DEACTIVATEDOC ) )
{
processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
}
else if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEDOCDONE )) ||
(rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEASDOCDONE )) ||
(rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVETODOCDONE )) )
{
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[ 0 ] <<= true;
processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
}
else if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEDOCFAILED )) ||
(rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEASDOCFAILED )) ||
(rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVETODOCFAILED )) )
{
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[ 0 ] <<= false;
processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
}
else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CLOSEDOC ) )
{
/* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
events and stop listening to the model (done in base class). */
uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
if( xController.is() )
{
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[ 0 ] <<= xController;
processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE, aArgs );
}
processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
}
else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_VIEWCREATED ) )
{
uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
if( mxListener.get() && xController.is() )
mxListener->startControllerListening( xController );
}
VbaEventsHelperBase::notifyEvent( rEvent );
}
// protected ------------------------------------------------------------------
bool ScVbaEventsHelper::implPrepareEvent( EventQueue& rEventQueue,
const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& rArgs ) throw (uno::RuntimeException)
{
// document and document shell are needed during event processing
if( !mpShell || !mpDoc )
throw uno::RuntimeException();
/* For document events: check if events are enabled via the
Application.EnableEvents symbol (this is an Excel-only attribute).
Check this again for every event, as the event handler may change the
state of the EnableEvents symbol. Global events such as AUTO_OPEN and
AUTO_CLOSE are always enabled. */
bool bExecuteEvent = (rInfo.mnModuleType != script::ModuleType::DOCUMENT) || ScVbaApplication::getDocumentEventsEnabled();
// framework and Calc fire a few events before 'OnLoad', ignore them
if( bExecuteEvent )
bExecuteEvent = (rInfo.mnEventId == WORKBOOK_OPEN) ? !mbOpened : mbOpened;
// special handling for some events
if( bExecuteEvent ) switch( rInfo.mnEventId )
{
case WORKBOOK_OPEN:
{
// execute delayed Activate event too (see above)
rEventQueue.push_back( WORKBOOK_ACTIVATE );
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[ 0 ] <<= mxModel->getCurrentController();
rEventQueue.push_back( EventQueueEntry( WORKBOOK_WINDOWACTIVATE, aArgs ) );
rEventQueue.push_back( AUTO_OPEN );
// remember initial selection
maOldSelection <<= mxModel->getCurrentSelection();
}
break;
case WORKSHEET_SELECTIONCHANGE:
// if selection is not changed, then do not fire the event
bExecuteEvent = isSelectionChanged( rArgs, 0 );
break;
}
if( bExecuteEvent )
{
// add workbook event associated to a sheet event
bool bSheetEvent = false;
if( (rInfo.maUserData >>= bSheetEvent) && bSheetEvent )
rEventQueue.push_back( EventQueueEntry( rInfo.mnEventId + USERDEFINED_START, rArgs ) );
}
return bExecuteEvent;
}
uno::Sequence< uno::Any > ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& rInfo,
const uno::Sequence< uno::Any >& rArgs ) throw (lang::IllegalArgumentException)
{
// fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
bool bSheetEventAsBookEvent = rInfo.mnEventId > USERDEFINED_START;
sal_Int32 nEventId = bSheetEventAsBookEvent ? (rInfo.mnEventId - USERDEFINED_START) : rInfo.mnEventId;
uno::Sequence< uno::Any > aVbaArgs;
switch( nEventId )
{
// *** Workbook ***
// no arguments
case WORKBOOK_ACTIVATE:
case WORKBOOK_DEACTIVATE:
case WORKBOOK_OPEN:
break;
// 1 arg: cancel
case WORKBOOK_BEFORECLOSE:
case WORKBOOK_BEFOREPRINT:
aVbaArgs.realloc( 1 );
// current cancel state will be inserted by caller
break;
// 2 args: saveAs, cancel
case WORKBOOK_BEFORESAVE:
aVbaArgs.realloc( 2 );
checkArgumentType< bool >( rArgs, 0 );
aVbaArgs[ 0 ] = rArgs[ 0 ];
// current cancel state will be inserted by caller
break;
// 1 arg: success
case WORKBOOK_AFTERSAVE:
aVbaArgs.realloc( 1 );
checkArgumentType< bool >( rArgs, 0 );
aVbaArgs[ 0 ] = rArgs[ 0 ];
break;
// 1 arg: window
case WORKBOOK_WINDOWACTIVATE:
case WORKBOOK_WINDOWDEACTIVATE:
case WORKBOOK_WINDOWRESIZE:
aVbaArgs.realloc( 1 );
aVbaArgs[ 0 ] = createWindow( rArgs, 0 );
break;
// 1 arg: worksheet
case WORKBOOK_NEWSHEET:
aVbaArgs.realloc( 1 );
aVbaArgs[ 0 ] = createWorksheet( rArgs, 0 );
break;
// *** Worksheet ***
// no arguments
case WORKSHEET_ACTIVATE:
case WORKSHEET_CALCULATE:
case WORKSHEET_DEACTIVATE:
break;
// 1 arg: range
case WORKSHEET_CHANGE:
case WORKSHEET_SELECTIONCHANGE:
aVbaArgs.realloc( 1 );
aVbaArgs[ 0 ] = createRange( rArgs, 0 );
break;
// 2 args: range, cancel
case WORKSHEET_BEFOREDOUBLECLICK:
case WORKSHEET_BEFORERIGHTCLICK:
aVbaArgs.realloc( 2 );
aVbaArgs[ 0 ] = createRange( rArgs, 0 );
// current cancel state will be inserted by caller
break;
// 1 arg: hyperlink
case WORKSHEET_FOLLOWHYPERLINK:
aVbaArgs.realloc( 1 );
aVbaArgs[ 0 ] = createHyperlink( rArgs, 0 );
break;
}
/* For workbook events associated to sheet events, the workbook event gets
the same arguments but with a Worksheet object in front of them. */
if( bSheetEventAsBookEvent )
{
sal_Int32 nLength = aVbaArgs.getLength();
uno::Sequence< uno::Any > aVbaArgs2( nLength + 1 );
aVbaArgs2[ 0 ] = createWorksheet( rArgs, 0 );
for( sal_Int32 nIndex = 0; nIndex < nLength; ++nIndex )
aVbaArgs2[ nIndex + 1 ] = aVbaArgs[ nIndex ];
aVbaArgs = aVbaArgs2;
}
return aVbaArgs;
}
void ScVbaEventsHelper::implPostProcessEvent( EventQueue& rEventQueue,
const EventHandlerInfo& rInfo, bool bCancel ) throw (uno::RuntimeException)
{
switch( rInfo.mnEventId )
{
case WORKBOOK_OPEN:
mbOpened = true;
// register the listeners
if( !mxListener.is() )
mxListener = new ScVbaEventListener( *this, mxModel, mpDocShell );
break;
case WORKBOOK_BEFORECLOSE:
/* Execute Auto_Close only if not cancelled by event handler, but
before UI asks user whether to cancel closing the document. */
if( !bCancel )
rEventQueue.push_back( AUTO_CLOSE );
break;
}
}
OUString ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& rInfo,
const uno::Sequence< uno::Any >& rArgs ) const throw (lang::IllegalArgumentException)
{
bool bSheetEvent = false;
rInfo.maUserData >>= bSheetEvent;
SCTAB nTab = bSheetEvent ? lclGetTabFromArgs( rArgs, 0 ) : -1;
if( bSheetEvent && (nTab < 0) )
throw lang::IllegalArgumentException();
String aCodeName;
if( bSheetEvent )
mpDoc->GetCodeName( nTab, aCodeName );
else
aCodeName = mpDoc->GetCodeName();
return aCodeName;
}
// private --------------------------------------------------------------------
namespace {
/** Compares the passed range lists representing sheet selections. Ignores
selections that refer to different sheets (returns false in this case). */
bool lclSelectionChanged( const ScRangeList& rLeft, const ScRangeList& rRight )
{
// one of the range lists empty? -> return false, if both lists empty
bool bLeftEmpty = rLeft.Count() == 0;
bool bRightEmpty = rRight.Count() == 0;
if( bLeftEmpty || bRightEmpty )
return !(bLeftEmpty && bRightEmpty);
// check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
if( rLeft.GetObject( 0 )->aStart.Tab() != rRight.GetObject( 0 )->aStart.Tab() )
return false;
// compare all ranges
return rLeft != rRight;
}
} // namespace
bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) throw (lang::IllegalArgumentException, uno::RuntimeException)
{
uno::Reference< uno::XInterface > xOldSelection( maOldSelection, uno::UNO_QUERY );
uno::Reference< uno::XInterface > xNewSelection = getXSomethingFromArgs< uno::XInterface >( rArgs, nIndex, false );
ScCellRangesBase* pOldCellRanges = ScCellRangesBase::getImplementation( xOldSelection );
ScCellRangesBase* pNewCellRanges = ScCellRangesBase::getImplementation( xNewSelection );
bool bChanged = !pOldCellRanges || !pNewCellRanges || lclSelectionChanged( pOldCellRanges->GetRangeList(), pNewCellRanges->GetRangeList() );
maOldSelection <<= xNewSelection;
return bChanged;
}
uno::Any ScVbaEventsHelper::createWorksheet( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
// extract sheet index, will throw, if parameter is invalid
SCTAB nTab = lclGetTabFromArgs( rArgs, nIndex );
return uno::Any( excel::getUnoSheetModuleObj( mxModel, nTab ) );
}
uno::Any ScVbaEventsHelper::createRange( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
// it is possible to pass an existing VBA Range object
uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
if( !xVbaRange.is() )
{
uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
uno::Reference< table::XCellRange > xRange = getXSomethingFromArgs< table::XCellRange >( rArgs, nIndex );
if ( !xRanges.is() && !xRange.is() )
throw lang::IllegalArgumentException();
uno::Sequence< uno::Any > aArgs( 2 );
if ( xRanges.is() )
{
aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRanges );
aArgs[ 1 ] <<= xRanges;
}
else
{
aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRange );
aArgs[ 1 ] <<= xRange;
}
xVbaRange.set( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Range", aArgs ), uno::UNO_QUERY_THROW );
}
return uno::Any( xVbaRange );
}
uno::Any ScVbaEventsHelper::createHyperlink( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
uno::Reference< table::XCell > xCell = getXSomethingFromArgs< table::XCell >( rArgs, nIndex, false );
uno::Sequence< uno::Any > aArgs( 2 );
aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xCell );
aArgs[ 1 ] <<= xCell;
uno::Reference< uno::XInterface > xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Hyperlink", aArgs ), uno::UNO_SET_THROW );
return uno::Any( xHyperlink );
}
uno::Any ScVbaEventsHelper::createWindow( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
uno::Sequence< uno::Any > aArgs( 3 );
aArgs[ 0 ] <<= getVBADocument( mxModel );
aArgs[ 1 ] <<= mxModel;
aArgs[ 2 ] <<= getXSomethingFromArgs< frame::XController >( rArgs, nIndex, false );
uno::Reference< uno::XInterface > xWindow( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Window", aArgs ), uno::UNO_SET_THROW );
return uno::Any( xWindow );
}
// ============================================================================
namespace vbaeventshelper
{
namespace sdecl = comphelper::service_decl;
sdecl::class_<ScVbaEventsHelper, sdecl::with_args<true> > serviceImpl;
extern sdecl::ServiceDecl const serviceDecl(
serviceImpl,
"ScVbaEventsHelper",
"com.sun.star.script.vba.VBASpreadsheetEventProcessor" );
}
// ============================================================================