mib17: #i112634# add VBA sheet event handling, based on a patch from Noel Power

This commit is contained in:
Daniel Rentz
2010-07-06 19:34:53 +02:00
parent b687b63611
commit c449069c06
10 changed files with 443 additions and 2 deletions

View File

@@ -64,6 +64,7 @@ interface XApplication
[attribute] any CutCopyMode;
[attribute] any StatusBar;
[attribute] long Cursor;
[attribute] boolean EnableEvents;
void setDefaultFilePath([in] string DefaultFilePath) raises(com::sun::star::script::BasicErrorException);

View File

@@ -29,7 +29,7 @@ PRJ=..$/..$/..
PRJNAME=oovapi
TARGET=excel
PACKAGE=ooo$/vba$/Excel
PACKAGE=ooo$/vba$/excel
# --- Settings -----------------------------------------------------
.INCLUDE : $(PRJ)$/util$/makefile.pmk

View File

@@ -29,7 +29,7 @@ PRJ=..$/..$/..
PRJNAME=oovapi
TARGET=word
PACKAGE=ooo$/vba$/Word
PACKAGE=ooo$/vba$/word
# --- Settings -----------------------------------------------------
.INCLUDE : $(PRJ)$/util$/makefile.pmk

View File

@@ -292,6 +292,7 @@ public:
sal_Bool IsReadOnlyUI() const;
void SetNoName();
sal_Bool IsInModalMode() const;
sal_Bool IsInPrepareClose() const;
//<!--Added by PengYunQuan for Validity Cell Range Picker
virtual sal_Bool AcceptStateUpdate() const;
//-->Added by PengYunQuan for Validity Cell Range Picker

View File

@@ -100,6 +100,7 @@
#include <com/sun/star/embed/XTransactedObject.hpp>
#include <com/sun/star/util/XCloneable.hpp>
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/script/vba/XCoreEventProcessor.hpp>
#include "helpid.hrc"
@@ -489,6 +490,22 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)
case SID_SAVEASDOC:
case SID_SAVEDOC:
{
// ask VBA emulation at document model whether to save the document
if( nId == SID_SAVEDOC || nId == SID_SAVEASDOC ) try
{
Reference< script::vba::XCoreEventProcessor > xEventProcessor( GetModel(), UNO_QUERY_THROW );
xEventProcessor->processCoreVbaEvent( nId );
}
catch( util::VetoException& )
{
// VBA event handler indicates to cancel saving the document
rReq.SetReturnValue( SfxBoolItem( 0, TRUE ) );
return;
}
catch( Exception& )
{
}
//!! detaillierte Auswertung eines Fehlercodes
SfxObjectShellRef xLock( this );
@@ -895,6 +912,13 @@ void SfxObjectShell::ExecFile_Impl(SfxRequest &rReq)
rReq.Done();
}
//-------------------------------------------------------------------------
sal_Bool SfxObjectShell::IsInPrepareClose() const
{
return pImp->bInPrepareClose;
}
//--------------------------------------------------------------------
void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)

View File

@@ -29,6 +29,7 @@
#include "precompiled_sfx2.hxx"
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/script/vba/XCoreEventProcessor.hpp>
#include <com/sun/star/view/PrintableState.hpp>
#include "com/sun/star/view/XRenderable.hpp"
@@ -697,6 +698,23 @@ void SfxViewShell::ExecPrint_Impl( SfxRequest &rReq )
case SID_PRINTDOCDIRECT:
{
SfxObjectShell* pDoc = GetObjectShell();
// ask VBA emulation at document model whether to print the document
try
{
uno::Reference< script::vba::XCoreEventProcessor > xEventProcessor( pDoc->GetModel(), uno::UNO_QUERY_THROW );
xEventProcessor->processCoreVbaEvent( nId );
}
catch( util::VetoException& )
{
// VBA event handler indicates to cancel printing the document
rReq.SetReturnValue( SfxBoolItem( 0, FALSE ) );
return;
}
catch( uno::Exception& )
{
}
bool bDetectHidden = ( !bSilent && pDoc );
if ( bDetectHidden && pDoc->QueryHiddenInformation( WhenPrinting, NULL ) != RET_YES )
break;

View File

@@ -0,0 +1,165 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
#ifndef VBAHELPER_VBAEVENTSHELPERBASE_HXX
#define VBAHELPER_VBAEVENTSHELPERBASE_HXX
#include <com/sun/star/lang/XEventListener.hpp>
#include <com/sun/star/script/vba/XEventProcessor.hpp>
#include <cppuhelper/implbase2.hxx>
#include <map>
#include <deque>
#include "vbahelper/vbahelper.hxx"
namespace com { namespace sun { namespace star {
namespace uno { class XComponentContext; }
} } }
// ============================================================================
typedef ::cppu::WeakImplHelper2< css::script::vba::XEventProcessor, css::lang::XEventListener > VbaEventsHelperBase_BASE;
class VBAHELPER_DLLPUBLIC VbaEventsHelperBase : public VbaEventsHelperBase_BASE
{
public:
VbaEventsHelperBase(
const css::uno::Sequence< css::uno::Any >& rArgs,
const css::uno::Reference< css::uno::XComponentContext >& xContext );
virtual ~VbaEventsHelperBase();
// XEventProcessor
virtual void SAL_CALL setIgnoreEvents( sal_Bool bIgnoreEvents ) throw (css::uno::RuntimeException);
virtual sal_Bool SAL_CALL getIgnoreEvents() throw (css::uno::RuntimeException);
virtual sal_Bool SAL_CALL hasVbaEventHandler( sal_Int32 nEventId, const css::uno::Sequence< css::uno::Any >& rArgs ) throw (css::lang::IllegalArgumentException, css::uno::RuntimeException);
virtual void SAL_CALL processVbaEvent( sal_Int32 nEventId, const css::uno::Sequence< css::uno::Any >& rArgs ) throw (css::lang::IllegalArgumentException, css::script::provider::ScriptFrameworkErrorException, css::util::VetoException, css::uno::RuntimeException);
// XEventListener
virtual void SAL_CALL disposing( const css::lang::EventObject& aSource ) throw (css::uno::RuntimeException);
protected:
// ------------------------------------------------------------------------
enum EventHandlerType { EVENTHANDLER_GLOBAL, EVENTHANDLER_DOCUMENT };
struct EventHandlerInfo
{
sal_Int32 mnEventId;
::rtl::OUString maMacroName;
EventHandlerType meType;
sal_Int32 mnCancelIndex;
css::uno::Any maUserData;
};
/** Registers a supported event handler.
@param nEventId Event identifier from com.sun.star.script.vba.EventIdentifier.
@param pcMacroName Name of the associated VBA event handler macro.
@param eType Document event or global event.
@param nCancelIndex 0-based index of Cancel parameter, or -1.
@param rUserData User data for free usage in derived implementations. */
void registerEventHandler(
sal_Int32 nEventId,
const sal_Char* pcMacroName,
EventHandlerType eType = EVENTHANDLER_DOCUMENT,
sal_Int32 nCancelIndex = -1,
const css::uno::Any& rUserData = css::uno::Any() );
/** Throws, if the passed sequence does not contain a value at the specified index. */
static inline void checkArgument( const css::uno::Sequence< css::uno::Any >& rArgs, sal_Int32 nIndex ) throw (css::lang::IllegalArgumentException)
{ if( rArgs.getLength() <= nIndex ) throw css::lang::IllegalArgumentException(); }
/** Throws, if the passed sequence does not contain a value of a specific at the specified index. */
template< typename Type >
static inline void checkArgumentType( const css::uno::Sequence< css::uno::Any >& rArgs, sal_Int32 nIndex ) throw (css::lang::IllegalArgumentException)
{ if( (rArgs.getLength() <= nIndex) || !rArgs[ nIndex ].has< Type >() ) throw css::lang::IllegalArgumentException(); }
// ------------------------------------------------------------------------
struct EventQueueEntry
{
sal_Int32 mnEventId;
css::uno::Sequence< css::uno::Any > maArgs;
inline /*implicit*/ EventQueueEntry( sal_Int32 nEventId ) : mnEventId( nEventId ) {}
inline EventQueueEntry( sal_Int32 nEventId, const css::uno::Sequence< css::uno::Any >& rArgs ) : mnEventId( nEventId ), maArgs( rArgs ) {}
};
typedef ::std::deque< EventQueueEntry > EventQueue;
/** Derived classes return whether event processing is enabled. Throws if
the instance is in an invalid state. */
virtual bool implEventsEnabled() throw (css::uno::RuntimeException) = 0;
/** Derived classes do additional prpeparations and return whether the
event handler has to be called. */
virtual bool implPrepareEvent(
EventQueue& rEventQueue,
const EventHandlerInfo& rInfo,
const css::uno::Sequence< css::uno::Any >& rArgs ) throw (css::uno::RuntimeException) = 0;
/** Derived classes have to return the argument list for the specified VBA event handler. */
virtual css::uno::Sequence< css::uno::Any > implBuildArgumentList(
const EventHandlerInfo& rInfo,
const css::uno::Sequence< css::uno::Any >& rArgs ) throw (css::lang::IllegalArgumentException) = 0;
/** Derived classes may do additional postprocessing. Called even if the
event handler does not exist, or if an error occured during execution. */
virtual void implPostProcessEvent(
EventQueue& rEventQueue,
const EventHandlerInfo& rInfo,
bool bSuccess,
bool bCancel ) throw (css::uno::RuntimeException) = 0;
/** Derived classes have to return the name of the Basic document module. */
virtual ::rtl::OUString implGetDocumentModuleName(
const EventHandlerInfo& rInfo,
const css::uno::Sequence< css::uno::Any >& rArgs ) const throw (css::lang::IllegalArgumentException) = 0;
private:
/** Returns the event handler info struct for the specified event, or throws. */
const EventHandlerInfo& getEventHandlerInfo( sal_Int32 nEventId ) const throw (css::lang::IllegalArgumentException);
/** Searches the event handler in the document and returns its full script path. */
::rtl::OUString getEventHandlerPath(
const EventHandlerInfo& rInfo,
const css::uno::Sequence< css::uno::Any >& rArgs ) const throw (css::lang::IllegalArgumentException);
/** Removes this instance from all broadcasters. */
void stopListening();
protected:
css::uno::Reference< css::frame::XModel > mxModel;
SfxObjectShell* mpShell;
private:
typedef ::std::map< sal_Int32, EventHandlerInfo > EventHandlerMap;
EventHandlerMap maEvents;
bool mbIgnoreEvents;
bool mbDisposed;
};
// ============================================================================
#endif

View File

@@ -26,3 +26,4 @@ mkdir: %_DEST%\inc%_EXT%\basic
..\inc\vbahelper\vbatextframe.hxx %_DEST%\inc%_EXT%\vbahelper\vbatextframe.hxx
..\inc\vbahelper\vbashaperange.hxx %_DEST%\inc%_EXT%\vbahelper\vbashaperange.hxx
..\inc\vbahelper\vbapagesetupbase.hxx %_DEST%\inc%_EXT%\vbahelper\vbapagesetupbase.hxx
..\inc\vbahelper\vbaeventshelperbase.hxx %_DEST%\inc%_EXT%\vbahelper\vbaeventshelperbase.hxx

View File

@@ -65,6 +65,8 @@ SLOFILES=\
$(SLO)$/vbashaperange.obj \
$(SLO)$/vbatextframe.obj \
$(SLO)$/vbapagesetupbase.obj \
$(SLO)$/vbaeventshelperbase.obj
# --- Targets -------------------------------------------------------
.INCLUDE : target.mk

View File

@@ -0,0 +1,229 @@
/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
#include "vbahelper/vbaeventshelperbase.hxx"
#include <filter/msfilter/msvbahelper.hxx>
using namespace ::com::sun::star;
using namespace ::ooo::vba;
// ============================================================================
VbaEventsHelperBase::VbaEventsHelperBase( const uno::Sequence< uno::Any >& rArgs, const uno::Reference< uno::XComponentContext >& /*xContext*/ ) :
mpShell( 0 ),
mbIgnoreEvents( false ),
mbDisposed( false )
{
try
{
mxModel = getXSomethingFromArgs< frame::XModel >( rArgs, 0, false );
mpShell = getSfxObjShell( mxModel );
// add dispose listener
uno::Reference< lang::XComponent > xComponent( mxModel, uno::UNO_QUERY_THROW );
xComponent->addEventListener( this );
}
catch( uno::Exception& )
{
}
}
VbaEventsHelperBase::~VbaEventsHelperBase()
{
stopListening();
}
void SAL_CALL VbaEventsHelperBase::setIgnoreEvents( sal_Bool bIgnoreEvents ) throw (uno::RuntimeException)
{
mbIgnoreEvents = bIgnoreEvents;
}
sal_Bool SAL_CALL VbaEventsHelperBase::getIgnoreEvents() throw (uno::RuntimeException)
{
return mbIgnoreEvents;
}
sal_Bool SAL_CALL VbaEventsHelperBase::hasVbaEventHandler( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
throw (lang::IllegalArgumentException, uno::RuntimeException)
{
// getEventHandlerInfo() throws, if unknown event dentifier has been passed
const EventHandlerInfo& rInfo = getEventHandlerInfo( nEventId );
// getEventHandlerPath() searches for the macro in the document
return getEventHandlerPath( rInfo, rArgs ).getLength() > 0;
}
void SAL_CALL VbaEventsHelperBase::processVbaEvent( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
throw (lang::IllegalArgumentException, script::provider::ScriptFrameworkErrorException, util::VetoException, uno::RuntimeException)
{
/* Derived classes may add new event identifiers to be processed while
processing the original event. All unprocessed events are collected in
a queue. First element in the queue is the next event to be processed. */
EventQueue aEventQueue;
aEventQueue.push_back( EventQueueEntry( nEventId, rArgs ) );
/* bEnabled will track if event processing is enabled. Every event handler
may disable handling of other events. */
bool bEnabled = !mbIgnoreEvents;
/* bCancel will contain the current Cancel value. It is possible that
multiple events will try to modify the Cancel value. Every event
handler receives the Cancel value of the previous event handler. */
bool bCancel = false;
/* bSuccess will change to true if at least one event handler has been
executed successfully. */
bool bSuccess = false;
/* Loop as long as there are more events to be processed, and as event
handling is still enabled. Derived classes may add new events to be
processed in the virtual implPrepareEvent() function. */
while( bEnabled && !aEventQueue.empty() )
{
/* Check that all class members are available, and that we are not
disposed (this may have happened at any time during execution of
the last event handler). */
if( mbDisposed || !mxModel.is() || !mpShell )
throw uno::RuntimeException();
// get info for next event
const EventHandlerInfo& rInfo = getEventHandlerInfo( aEventQueue.front().mnEventId );
uno::Sequence< uno::Any > aEventArgs = aEventQueue.front().maArgs;
aEventQueue.pop_front();
// let derived classes decide whether event processing is still enabled
bEnabled = implEventsEnabled();
// let derived classes prepare the event, they may add new events for next iteration
if( bEnabled && implPrepareEvent( aEventQueue, rInfo, aEventArgs ) )
{
// search the event handler macro in the document
::rtl::OUString aMacroPath = getEventHandlerPath( rInfo, aEventArgs );
bool bEventSuccess = false;
if( aMacroPath.getLength() > 0 )
{
// build the argument list
uno::Sequence< uno::Any > aVbaArgs = implBuildArgumentList( rInfo, aEventArgs );
// insert current cancel value
if( rInfo.mnCancelIndex >= 0 )
{
if( rInfo.mnCancelIndex >= aVbaArgs.getLength() )
throw lang::IllegalArgumentException();
aVbaArgs[ rInfo.mnCancelIndex ] <<= bCancel;
}
// execute the event handler
uno::Any aRet, aCaller;
bEventSuccess = executeMacro( mpShell, aMacroPath, aVbaArgs, aRet, aCaller );
// extract new cancel value
if( rInfo.mnCancelIndex >= 0 )
{
if( rInfo.mnCancelIndex >= aVbaArgs.getLength() )
throw lang::IllegalArgumentException();
// cancel value may be boolean or any integer type, Any(bool) does not extract to sal_Int32
bool bNewCancel = false;
sal_Int32 nNewCancel = 0;
if( aVbaArgs[ rInfo.mnCancelIndex ] >>= bNewCancel )
bCancel = bNewCancel;
else if( aVbaArgs[ rInfo.mnCancelIndex ] >>= nNewCancel )
bCancel = nNewCancel != 0;
}
}
// post processing (also, if event handler does not exist, or on error
implPostProcessEvent( aEventQueue, rInfo, bEventSuccess, bCancel );
// global success, if at least one event handler succeeded
bSuccess |= bEventSuccess;
}
}
// if event handlers want to cancel the event, do so regardless of any errors
if( bCancel )
throw util::VetoException();
// if no event handler finished successfully, throw
if( !bSuccess )
throw script::provider::ScriptFrameworkErrorException();
}
void SAL_CALL VbaEventsHelperBase::disposing( const lang::EventObject& /*aSource*/ ) throw (uno::RuntimeException)
{
OSL_TRACE( "VbaEventsHelperBase::disposing" );
stopListening();
mbDisposed = true;
}
// protected ------------------------------------------------------------------
void VbaEventsHelperBase::registerEventHandler( sal_Int32 nEventId,
const sal_Char* pcMacroName, EventHandlerType eType, sal_Int32 nCancelIndex, const uno::Any& rUserData )
{
EventHandlerInfo& rInfo = maEvents[ nEventId ];
rInfo.mnEventId = nEventId;
rInfo.maMacroName = ::rtl::OUString::createFromAscii( pcMacroName );
rInfo.meType = eType;
rInfo.mnCancelIndex = nCancelIndex;
rInfo.maUserData = rUserData;
}
// private --------------------------------------------------------------------
const VbaEventsHelperBase::EventHandlerInfo& VbaEventsHelperBase::getEventHandlerInfo(
sal_Int32 nEventId ) const throw (lang::IllegalArgumentException)
{
EventHandlerMap::const_iterator aIt = maEvents.find( nEventId );
if( aIt == maEvents.end() )
throw lang::IllegalArgumentException();
return aIt->second;
}
::rtl::OUString VbaEventsHelperBase::getEventHandlerPath( const EventHandlerInfo& rInfo,
const uno::Sequence< uno::Any >& rArgs ) const throw (lang::IllegalArgumentException)
{
::rtl::OUString aMacroName;
switch( rInfo.meType )
{
case EVENTHANDLER_GLOBAL:
aMacroName = rInfo.maMacroName;
break;
case EVENTHANDLER_DOCUMENT:
aMacroName = ::rtl::OUStringBuffer( implGetDocumentModuleName( rInfo, rArgs ) ).
append( sal_Unicode( '.' ) ).append( rInfo.maMacroName ).makeStringAndClear();
break;
}
return resolveVBAMacro( mpShell, aMacroName ).ResolvedMacro();
}
void VbaEventsHelperBase::stopListening()
{
if( !mbDisposed ) try
{
uno::Reference< lang::XComponent > xComponent( mxModel, uno::UNO_QUERY_THROW );
xComponent->removeEventListener( this );
}
catch( uno::Exception& )
{
}
}
// ============================================================================