1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "vbaeventshelper.hxx"
22 #include <com/sun/star/awt/XTopWindow.hpp>
23 #include <com/sun/star/awt/XTopWindowListener.hpp>
24 #include <com/sun/star/awt/XWindowListener.hpp>
25 #include <com/sun/star/frame/XBorderResizeListener.hpp>
26 #include <com/sun/star/frame/XControllerBorder.hpp>
27 #include <com/sun/star/script/ModuleType.hpp>
28 #include <com/sun/star/script/vba/VBAEventId.hpp>
29 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
30 #include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
31 #include <com/sun/star/table/XCellRange.hpp>
32 #include <com/sun/star/util/XChangesListener.hpp>
33 #include <com/sun/star/util/XChangesNotifier.hpp>
35 #include <cppuhelper/implbase4.hxx>
36 #include <toolkit/helper/vclunohelper.hxx>
37 #include <unotools/eventcfg.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/window.hxx>
41 #include "cellsuno.hxx"
42 #include "convuno.hxx"
43 #include "vbaapplication.hxx"
45 using namespace ::com::sun::star
;
46 using namespace ::com::sun::star::script::vba::VBAEventId
;
47 using namespace ::ooo::vba
;
51 /** Extracts a sheet index from the specified element of the passed sequence.
52 The element may be an integer, a Calc range or ranges object, or a VBA Range object. */
53 SCTAB
lclGetTabFromArgs( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
55 VbaEventsHelperBase::checkArgument( rArgs
, nIndex
);
57 // first try to extract a sheet index
59 if( rArgs
[ nIndex
] >>= nTab
)
61 if( (nTab
< 0) || (nTab
> MAXTAB
) )
62 throw lang::IllegalArgumentException();
63 return static_cast< SCTAB
>( nTab
);
66 // try VBA Range object
67 uno::Reference
< excel::XRange
> xVbaRange
= getXSomethingFromArgs
< excel::XRange
>( rArgs
, nIndex
);
70 uno::Reference
< XHelperInterface
> xVbaHelper( xVbaRange
, uno::UNO_QUERY_THROW
);
71 // TODO: in the future, the parent may be an excel::XChart (chart sheet) -> will there be a common base interface?
72 uno::Reference
< excel::XWorksheet
> xVbaSheet( xVbaHelper
->getParent(), uno::UNO_QUERY_THROW
);
73 // VBA sheet index is 1-based
74 return static_cast< SCTAB
>( xVbaSheet
->getIndex() - 1 );
77 // try single UNO range object
78 uno::Reference
< sheet::XCellRangeAddressable
> xCellRangeAddressable
= getXSomethingFromArgs
< sheet::XCellRangeAddressable
>( rArgs
, nIndex
);
79 if( xCellRangeAddressable
.is() )
80 return xCellRangeAddressable
->getRangeAddress().Sheet
;
82 // at last, try UNO range list
83 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges
= getXSomethingFromArgs
< sheet::XSheetCellRangeContainer
>( rArgs
, nIndex
);
86 uno::Sequence
< table::CellRangeAddress
> aRangeAddresses
= xRanges
->getRangeAddresses();
87 if( aRangeAddresses
.getLength() > 0 )
88 return aRangeAddresses
[ 0 ].Sheet
;
91 throw lang::IllegalArgumentException();
94 /** Returns the AWT container window of the passed controller. */
95 uno::Reference
< awt::XWindow
> lclGetWindowForController( const uno::Reference
< frame::XController
>& rxController
)
97 if( rxController
.is() ) try
99 uno::Reference
< frame::XFrame
> xFrame( rxController
->getFrame(), uno::UNO_SET_THROW
);
100 return xFrame
->getContainerWindow();
102 catch( uno::Exception
& )
110 typedef ::cppu::WeakImplHelper4
< awt::XTopWindowListener
, awt::XWindowListener
, frame::XBorderResizeListener
, util::XChangesListener
> ScVbaEventListener_BASE
;
112 // This class is to process Workbook window related event
113 class ScVbaEventListener
: public ScVbaEventListener_BASE
116 ScVbaEventListener( ScVbaEventsHelper
& rVbaEvents
, const uno::Reference
< frame::XModel
>& rxModel
, ScDocShell
* pDocShell
);
117 virtual ~ScVbaEventListener();
119 /** Starts listening to the passed document controller. */
120 void startControllerListening( const uno::Reference
< frame::XController
>& rxController
);
121 /** Stops listening to the passed document controller. */
122 void stopControllerListening( const uno::Reference
< frame::XController
>& rxController
);
124 // XTopWindowListener
125 virtual void SAL_CALL
windowOpened( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
126 virtual void SAL_CALL
windowClosing( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
127 virtual void SAL_CALL
windowClosed( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
128 virtual void SAL_CALL
windowMinimized( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
129 virtual void SAL_CALL
windowNormalized( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
130 virtual void SAL_CALL
windowActivated( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
131 virtual void SAL_CALL
windowDeactivated( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
134 virtual void SAL_CALL
windowResized( const awt::WindowEvent
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
135 virtual void SAL_CALL
windowMoved( const awt::WindowEvent
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
136 virtual void SAL_CALL
windowShown( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
137 virtual void SAL_CALL
windowHidden( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
139 // XBorderResizeListener
140 virtual void SAL_CALL
borderWidthsChanged( const uno::Reference
< uno::XInterface
>& rSource
, const frame::BorderWidths
& aNewSize
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
143 virtual void SAL_CALL
changesOccurred( const util::ChangesEvent
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
146 virtual void SAL_CALL
disposing( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
149 /** Starts listening to the document model. */
150 void startModelListening();
151 /** Stops listening to the document model. */
152 void stopModelListening();
154 /** Returns the controller for the passed VCL window. */
155 uno::Reference
< frame::XController
> getControllerForWindow( vcl::Window
* pWindow
) const;
157 /** Calls the Workbook_Window[Activate|Deactivate] event handler. */
158 void processWindowActivateEvent( vcl::Window
* pWindow
, bool bActivate
);
159 /** Posts a Workbook_WindowResize user event. */
160 void postWindowResizeEvent( vcl::Window
* pWindow
);
161 /** Callback link for Application::PostUserEvent(). */
162 DECL_LINK( processWindowResizeEvent
, vcl::Window
* );
165 typedef ::std::map
< VclPtr
<vcl::Window
>, uno::Reference
< frame::XController
> > WindowControllerMap
;
167 ::osl::Mutex maMutex
;
168 ScVbaEventsHelper
& mrVbaEvents
;
169 uno::Reference
< frame::XModel
> mxModel
;
170 ScDocShell
* mpDocShell
;
171 WindowControllerMap maControllers
; /// Maps VCL top windows to their controllers.
172 std::multiset
< VclPtr
<vcl::Window
> > m_PostedWindows
; /// Keeps processWindowResizeEvent windows from being deleted between postWindowResizeEvent and processWindowResizeEvent
173 VclPtr
<vcl::Window
> mpActiveWindow
; /// Currently activated window, to prevent multiple (de)activation.
174 bool mbWindowResized
; /// True = window resize system event processed.
175 bool mbBorderChanged
; /// True = borders changed system event processed.
179 ScVbaEventListener::ScVbaEventListener( ScVbaEventsHelper
& rVbaEvents
, const uno::Reference
< frame::XModel
>& rxModel
, ScDocShell
* pDocShell
) :
180 mrVbaEvents( rVbaEvents
),
182 mpDocShell( pDocShell
),
184 mbWindowResized( false ),
185 mbBorderChanged( false ),
186 mbDisposed( !rxModel
.is() )
191 startModelListening();
194 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController(), uno::UNO_QUERY_THROW
);
195 startControllerListening( xController
);
197 catch( uno::Exception
& )
202 ScVbaEventListener::~ScVbaEventListener()
206 void ScVbaEventListener::startControllerListening( const uno::Reference
< frame::XController
>& rxController
)
208 ::osl::MutexGuard
aGuard( maMutex
);
210 uno::Reference
< awt::XWindow
> xWindow
= lclGetWindowForController( rxController
);
212 try { xWindow
->addWindowListener( this ); } catch( uno::Exception
& ) {}
214 uno::Reference
< awt::XTopWindow
> xTopWindow( xWindow
, uno::UNO_QUERY
);
215 if( xTopWindow
.is() )
216 try { xTopWindow
->addTopWindowListener( this ); } catch( uno::Exception
& ) {}
218 uno::Reference
< frame::XControllerBorder
> xControllerBorder( rxController
, uno::UNO_QUERY
);
219 if( xControllerBorder
.is() )
220 try { xControllerBorder
->addBorderResizeListener( this ); } catch( uno::Exception
& ) {}
222 if( vcl::Window
* pWindow
= VCLUnoHelper::GetWindow( xWindow
) )
224 maControllers
[ pWindow
] = rxController
;
228 void ScVbaEventListener::stopControllerListening( const uno::Reference
< frame::XController
>& rxController
)
230 ::osl::MutexGuard
aGuard( maMutex
);
232 uno::Reference
< awt::XWindow
> xWindow
= lclGetWindowForController( rxController
);
234 try { xWindow
->removeWindowListener( this ); } catch( uno::Exception
& ) {}
236 uno::Reference
< awt::XTopWindow
> xTopWindow( xWindow
, uno::UNO_QUERY
);
237 if( xTopWindow
.is() )
238 try { xTopWindow
->removeTopWindowListener( this ); } catch( uno::Exception
& ) {}
240 uno::Reference
< frame::XControllerBorder
> xControllerBorder( rxController
, uno::UNO_QUERY
);
241 if( xControllerBorder
.is() )
242 try { xControllerBorder
->removeBorderResizeListener( this ); } catch( uno::Exception
& ) {}
244 if( vcl::Window
* pWindow
= VCLUnoHelper::GetWindow( xWindow
) )
246 maControllers
.erase( pWindow
);
247 if( pWindow
== mpActiveWindow
)
252 void SAL_CALL
ScVbaEventListener::windowOpened( const lang::EventObject
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
256 void SAL_CALL
ScVbaEventListener::windowClosing( const lang::EventObject
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
260 void SAL_CALL
ScVbaEventListener::windowClosed( const lang::EventObject
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
264 void SAL_CALL
ScVbaEventListener::windowMinimized( const lang::EventObject
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
268 void SAL_CALL
ScVbaEventListener::windowNormalized( const lang::EventObject
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
272 void SAL_CALL
ScVbaEventListener::windowActivated( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
)
274 ::osl::MutexGuard
aGuard( maMutex
);
278 uno::Reference
< awt::XWindow
> xWindow( rEvent
.Source
, uno::UNO_QUERY
);
279 vcl::Window
* pWindow
= VCLUnoHelper::GetWindow( xWindow
);
280 OSL_TRACE( "ScVbaEventListener::windowActivated - pWindow = 0x%p, mpActiveWindow = 0x%p", pWindow
, mpActiveWindow
.get() );
281 // do not fire activation event multiple time for the same window
282 if( pWindow
&& (pWindow
!= mpActiveWindow
) )
284 // if another window is active, fire deactivation event first
286 processWindowActivateEvent( mpActiveWindow
, false );
287 // fire activation event for the new window
288 processWindowActivateEvent( pWindow
, true );
289 mpActiveWindow
= pWindow
;
294 void SAL_CALL
ScVbaEventListener::windowDeactivated( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
)
296 ::osl::MutexGuard
aGuard( maMutex
);
300 uno::Reference
< awt::XWindow
> xWindow( rEvent
.Source
, uno::UNO_QUERY
);
301 vcl::Window
* pWindow
= VCLUnoHelper::GetWindow( xWindow
);
302 OSL_TRACE( "ScVbaEventListener::windowDeactivated - pWindow = 0x%p, mpActiveWindow = 0x%p", pWindow
, mpActiveWindow
.get() );
303 // do not fire the deactivation event, if the window is not active (prevent multiple deactivation)
304 if( pWindow
&& (pWindow
== mpActiveWindow
) )
305 processWindowActivateEvent( pWindow
, false );
306 // forget pointer to the active window
311 void SAL_CALL
ScVbaEventListener::windowResized( const awt::WindowEvent
& rEvent
) throw (uno::RuntimeException
, std::exception
)
313 ::osl::MutexGuard
aGuard( maMutex
);
315 mbWindowResized
= true;
316 if( !mbDisposed
&& mbBorderChanged
)
318 uno::Reference
< awt::XWindow
> xWindow( rEvent
.Source
, uno::UNO_QUERY
);
319 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow
) );
323 void SAL_CALL
ScVbaEventListener::windowMoved( const awt::WindowEvent
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
327 void SAL_CALL
ScVbaEventListener::windowShown( const lang::EventObject
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
331 void SAL_CALL
ScVbaEventListener::windowHidden( const lang::EventObject
& /*rEvent*/ ) throw (uno::RuntimeException
, std::exception
)
335 void SAL_CALL
ScVbaEventListener::borderWidthsChanged( const uno::Reference
< uno::XInterface
>& rSource
, const frame::BorderWidths
& /*aNewSize*/ ) throw (uno::RuntimeException
, std::exception
)
337 ::osl::MutexGuard
aGuard( maMutex
);
339 mbBorderChanged
= true;
340 if( !mbDisposed
&& mbWindowResized
)
342 uno::Reference
< frame::XController
> xController( rSource
, uno::UNO_QUERY
);
343 uno::Reference
< awt::XWindow
> xWindow
= lclGetWindowForController( xController
);
344 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow
) );
348 void SAL_CALL
ScVbaEventListener::changesOccurred( const util::ChangesEvent
& rEvent
) throw (uno::RuntimeException
, std::exception
)
350 ::osl::MutexGuard
aGuard( maMutex
);
352 sal_Int32 nCount
= rEvent
.Changes
.getLength();
353 if( mbDisposed
|| !mpDocShell
|| (nCount
== 0) )
356 util::ElementChange aChange
= rEvent
.Changes
[ 0 ];
358 aChange
.Accessor
>>= sOperation
;
359 if( !sOperation
.equalsIgnoreAsciiCase("cell-change") )
364 uno::Reference
< table::XCellRange
> xRangeObj
;
365 aChange
.ReplacedElement
>>= xRangeObj
;
368 uno::Sequence
< uno::Any
> aArgs( 1 );
369 aArgs
[0] <<= xRangeObj
;
370 mrVbaEvents
.processVbaEventNoThrow( WORKSHEET_CHANGE
, aArgs
);
375 ScRangeList aRangeList
;
376 for( sal_Int32 nIndex
= 0; nIndex
< nCount
; ++nIndex
)
378 aChange
= rEvent
.Changes
[ nIndex
];
379 aChange
.Accessor
>>= sOperation
;
380 uno::Reference
< table::XCellRange
> xRangeObj
;
381 aChange
.ReplacedElement
>>= xRangeObj
;
382 if( xRangeObj
.is() && sOperation
.equalsIgnoreAsciiCase("cell-change") )
384 uno::Reference
< sheet::XCellRangeAddressable
> xCellRangeAddressable( xRangeObj
, uno::UNO_QUERY
);
385 if( xCellRangeAddressable
.is() )
388 ScUnoConversion::FillScRange( aRange
, xCellRangeAddressable
->getRangeAddress() );
389 aRangeList
.Append( aRange
);
394 if (!aRangeList
.empty())
396 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges( new ScCellRangesObj( mpDocShell
, aRangeList
) );
397 uno::Sequence
< uno::Any
> aArgs(1);
398 aArgs
[0] <<= xRanges
;
399 mrVbaEvents
.processVbaEventNoThrow( WORKSHEET_CHANGE
, aArgs
);
403 void SAL_CALL
ScVbaEventListener::disposing( const lang::EventObject
& rEvent
) throw (uno::RuntimeException
, std::exception
)
405 ::osl::MutexGuard
aGuard( maMutex
);
407 uno::Reference
< frame::XModel
> xModel( rEvent
.Source
, uno::UNO_QUERY
);
410 OSL_ENSURE( xModel
.get() == mxModel
.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
411 stopModelListening();
416 uno::Reference
< frame::XController
> xController( rEvent
.Source
, uno::UNO_QUERY
);
417 if( xController
.is() )
419 stopControllerListening( xController
);
424 // private --------------------------------------------------------------------
426 void ScVbaEventListener::startModelListening()
430 uno::Reference
< util::XChangesNotifier
> xChangesNotifier( mxModel
, uno::UNO_QUERY_THROW
);
431 xChangesNotifier
->addChangesListener( this );
433 catch( uno::Exception
& )
438 void ScVbaEventListener::stopModelListening()
442 uno::Reference
< util::XChangesNotifier
> xChangesNotifier( mxModel
, uno::UNO_QUERY_THROW
);
443 xChangesNotifier
->removeChangesListener( this );
445 catch( uno::Exception
& )
450 uno::Reference
< frame::XController
> ScVbaEventListener::getControllerForWindow( vcl::Window
* pWindow
) const
452 WindowControllerMap::const_iterator aIt
= maControllers
.find( pWindow
);
453 return (aIt
== maControllers
.end()) ? uno::Reference
< frame::XController
>() : aIt
->second
;
456 void ScVbaEventListener::processWindowActivateEvent( vcl::Window
* pWindow
, bool bActivate
)
458 uno::Reference
< frame::XController
> xController
= getControllerForWindow( pWindow
);
459 if( xController
.is() )
461 uno::Sequence
< uno::Any
> aArgs( 1 );
462 aArgs
[ 0 ] <<= xController
;
463 mrVbaEvents
.processVbaEventNoThrow( bActivate
? WORKBOOK_WINDOWACTIVATE
: WORKBOOK_WINDOWDEACTIVATE
, aArgs
);
467 void ScVbaEventListener::postWindowResizeEvent( vcl::Window
* pWindow
)
469 // check that the passed window is still alive (it must be registered in maControllers)
470 if( pWindow
&& (maControllers
.count( pWindow
) > 0) )
472 mbWindowResized
= mbBorderChanged
= false;
473 acquire(); // ensure we don't get deleted before the timer fires
474 m_PostedWindows
.insert(pWindow
);
475 Application::PostUserEvent( LINK( this, ScVbaEventListener
, processWindowResizeEvent
), pWindow
);
479 IMPL_LINK( ScVbaEventListener
, processWindowResizeEvent
, vcl::Window
*, pWindow
)
481 ::osl::MutexGuard
aGuard( maMutex
);
483 /* Check that the passed window is still alive (it must be registered in
484 maControllers). While closing a document, postWindowResizeEvent() may
485 be called on the last window which posts a user event via
486 Application::PostUserEvent to call this event handler. VCL will trigger
487 the handler some time later. Sometimes, the window gets deleted before.
488 This is handled via the disposing() function which removes the window
489 pointer from the member maControllers. Thus, checking whether
490 maControllers contains pWindow ensures that the window is still alive. */
491 if( !mbDisposed
&& pWindow
&& !pWindow
->IsDisposed() && (maControllers
.count(pWindow
) > 0) )
493 // do not fire event unless all mouse buttons have been released
494 vcl::Window::PointerState aPointerState
= pWindow
->GetPointerState();
495 if( (aPointerState
.mnState
& (MOUSE_LEFT
| MOUSE_MIDDLE
| MOUSE_RIGHT
)) == 0 )
497 uno::Reference
< frame::XController
> xController
= getControllerForWindow( pWindow
);
498 if( xController
.is() )
500 uno::Sequence
< uno::Any
> aArgs( 1 );
501 aArgs
[ 0 ] <<= xController
;
502 // #163419# do not throw exceptions into application core
503 mrVbaEvents
.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE
, aArgs
);
508 // note: there may be multiple processWindowResizeEvent outstanding
509 // for pWindow, so it may have been added to m_PostedWindows multiple
510 // times - so this must delete exactly one of these elements!
511 auto const iter(m_PostedWindows
.find(pWindow
));
512 assert(iter
!= m_PostedWindows
.end());
513 m_PostedWindows
.erase(iter
);
519 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence
< uno::Any
>& rArgs
, const uno::Reference
< uno::XComponentContext
>& xContext
) :
520 VbaEventsHelperBase( rArgs
, xContext
),
523 mpDocShell
= dynamic_cast< ScDocShell
* >( mpShell
); // mpShell from base class
524 mpDoc
= mpDocShell
? &mpDocShell
->GetDocument() : 0;
526 if( !mxModel
.is() || !mpDocShell
|| !mpDoc
)
529 #define REGISTER_EVENT( eventid, moduletype, classname, eventname, cancelindex, worksheet ) \
530 registerEventHandler( eventid, moduletype, classname "_" eventname, cancelindex, uno::Any( worksheet ) )
531 #define REGISTER_AUTO_EVENT( eventid, eventname ) \
532 REGISTER_EVENT( AUTO_##eventid, script::ModuleType::NORMAL, "Auto", eventname, -1, false )
533 #define REGISTER_WORKBOOK_EVENT( eventid, eventname, cancelindex ) \
534 REGISTER_EVENT( WORKBOOK_##eventid, script::ModuleType::DOCUMENT, "Workbook", eventname, cancelindex, false )
535 #define REGISTER_WORKSHEET_EVENT( eventid, eventname, cancelindex ) \
536 REGISTER_EVENT( WORKSHEET_##eventid, script::ModuleType::DOCUMENT, "Worksheet", eventname, cancelindex, true ); \
537 REGISTER_EVENT( (USERDEFINED_START + WORKSHEET_##eventid), script::ModuleType::DOCUMENT, "Workbook", "Sheet" eventname, (((cancelindex) >= 0) ? ((cancelindex) + 1) : -1), false )
540 REGISTER_AUTO_EVENT( OPEN
, "Open" );
541 REGISTER_AUTO_EVENT( CLOSE
, "Close" );
544 REGISTER_WORKBOOK_EVENT( ACTIVATE
, "Activate", -1 );
545 REGISTER_WORKBOOK_EVENT( DEACTIVATE
, "Deactivate", -1 );
546 REGISTER_WORKBOOK_EVENT( OPEN
, "Open", -1 );
547 REGISTER_WORKBOOK_EVENT( BEFORECLOSE
, "BeforeClose", 0 );
548 REGISTER_WORKBOOK_EVENT( BEFOREPRINT
, "BeforePrint", 0 );
549 REGISTER_WORKBOOK_EVENT( BEFORESAVE
, "BeforeSave", 1 );
550 REGISTER_WORKBOOK_EVENT( AFTERSAVE
, "AfterSave", -1 );
551 REGISTER_WORKBOOK_EVENT( NEWSHEET
, "NewSheet", -1 );
552 REGISTER_WORKBOOK_EVENT( WINDOWACTIVATE
, "WindowActivate", -1 );
553 REGISTER_WORKBOOK_EVENT( WINDOWDEACTIVATE
, "WindowDeactivate", -1 );
554 REGISTER_WORKBOOK_EVENT( WINDOWRESIZE
, "WindowResize", -1 );
556 // Worksheet events. All events have a corresponding workbook event.
557 REGISTER_WORKSHEET_EVENT( ACTIVATE
, "Activate", -1 );
558 REGISTER_WORKSHEET_EVENT( DEACTIVATE
, "Deactivate", -1 );
559 REGISTER_WORKSHEET_EVENT( BEFOREDOUBLECLICK
, "BeforeDoubleClick", 1 );
560 REGISTER_WORKSHEET_EVENT( BEFORERIGHTCLICK
, "BeforeRightClick", 1 );
561 REGISTER_WORKSHEET_EVENT( CALCULATE
, "Calculate", -1 );
562 REGISTER_WORKSHEET_EVENT( CHANGE
, "Change", -1 );
563 REGISTER_WORKSHEET_EVENT( SELECTIONCHANGE
, "SelectionChange", -1 );
564 REGISTER_WORKSHEET_EVENT( FOLLOWHYPERLINK
, "FollowHyperlink", -1 );
566 #undef REGISTER_WORKSHEET_EVENT
567 #undef REGISTER_WORKBOOK_EVENT
568 #undef REGISTER_AUTO_EVENT
569 #undef REGISTER_EVENT
572 ScVbaEventsHelper::~ScVbaEventsHelper()
576 void SAL_CALL
ScVbaEventsHelper::notifyEvent( const css::document::EventObject
& rEvent
) throw (css::uno::RuntimeException
, std::exception
)
578 static const uno::Sequence
< uno::Any
> saEmptyArgs
;
579 if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC
)) ||
580 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::CREATEDOC
)) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
582 processVbaEventNoThrow( WORKBOOK_OPEN
, saEmptyArgs
);
584 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC
) )
586 processVbaEventNoThrow( WORKBOOK_ACTIVATE
, saEmptyArgs
);
588 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC
) )
590 processVbaEventNoThrow( WORKBOOK_DEACTIVATE
, saEmptyArgs
);
592 else if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCDONE
)) ||
593 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCDONE
)) ||
594 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCDONE
)) )
596 uno::Sequence
< uno::Any
> aArgs( 1 );
598 processVbaEventNoThrow( WORKBOOK_AFTERSAVE
, aArgs
);
600 else if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCFAILED
)) ||
601 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCFAILED
)) ||
602 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCFAILED
)) )
604 uno::Sequence
< uno::Any
> aArgs( 1 );
605 aArgs
[ 0 ] <<= false;
606 processVbaEventNoThrow( WORKBOOK_AFTERSAVE
, aArgs
);
608 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC
) )
610 /* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
611 events and stop listening to the model (done in base class). */
612 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController() );
613 if( xController
.is() )
615 uno::Sequence
< uno::Any
> aArgs( 1 );
616 aArgs
[ 0 ] <<= xController
;
617 processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE
, aArgs
);
619 processVbaEventNoThrow( WORKBOOK_DEACTIVATE
, saEmptyArgs
);
621 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED
) )
623 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController() );
624 if( mxListener
.get() && xController
.is() )
625 mxListener
->startControllerListening( xController
);
627 VbaEventsHelperBase::notifyEvent( rEvent
);
630 OUString
ScVbaEventsHelper::getImplementationName()
631 throw (css::uno::RuntimeException
, std::exception
)
633 return OUString("ScVbaEventsHelper");
636 css::uno::Sequence
<OUString
> ScVbaEventsHelper::getSupportedServiceNames()
637 throw (css::uno::RuntimeException
, std::exception
)
639 return css::uno::Sequence
<OUString
>{
640 "com.sun.star.script.vba.VBASpreadsheetEventProcessor"};
643 // protected ------------------------------------------------------------------
645 bool ScVbaEventsHelper::implPrepareEvent( EventQueue
& rEventQueue
,
646 const EventHandlerInfo
& rInfo
, const uno::Sequence
< uno::Any
>& rArgs
) throw (uno::RuntimeException
)
648 // document and document shell are needed during event processing
649 if( !mpShell
|| !mpDoc
)
650 throw uno::RuntimeException();
652 /* For document events: check if events are enabled via the
653 Application.EnableEvents symbol (this is an Excel-only attribute).
654 Check this again for every event, as the event handler may change the
655 state of the EnableEvents symbol. Global events such as AUTO_OPEN and
656 AUTO_CLOSE are always enabled. */
657 bool bExecuteEvent
= (rInfo
.mnModuleType
!= script::ModuleType::DOCUMENT
) || ScVbaApplication::getDocumentEventsEnabled();
659 // framework and Calc fire a few events before 'OnLoad', ignore them
661 bExecuteEvent
= (rInfo
.mnEventId
== WORKBOOK_OPEN
) ? !mbOpened
: mbOpened
;
663 // special handling for some events
664 if( bExecuteEvent
) switch( rInfo
.mnEventId
)
668 // execute delayed Activate event too (see above)
669 rEventQueue
.push_back( WORKBOOK_ACTIVATE
);
670 uno::Sequence
< uno::Any
> aArgs( 1 );
671 aArgs
[ 0 ] <<= mxModel
->getCurrentController();
672 rEventQueue
.push_back( EventQueueEntry( WORKBOOK_WINDOWACTIVATE
, aArgs
) );
673 rEventQueue
.push_back( AUTO_OPEN
);
674 // remember initial selection
675 maOldSelection
<<= mxModel
->getCurrentSelection();
678 case WORKSHEET_SELECTIONCHANGE
:
679 // if selection is not changed, then do not fire the event
680 bExecuteEvent
= isSelectionChanged( rArgs
, 0 );
686 // add workbook event associated to a sheet event
687 bool bSheetEvent
= false;
688 if( (rInfo
.maUserData
>>= bSheetEvent
) && bSheetEvent
)
689 rEventQueue
.push_back( EventQueueEntry( rInfo
.mnEventId
+ USERDEFINED_START
, rArgs
) );
692 return bExecuteEvent
;
695 uno::Sequence
< uno::Any
> ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo
& rInfo
,
696 const uno::Sequence
< uno::Any
>& rArgs
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
698 // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
699 bool bSheetEventAsBookEvent
= rInfo
.mnEventId
> USERDEFINED_START
;
700 sal_Int32 nEventId
= bSheetEventAsBookEvent
? (rInfo
.mnEventId
- USERDEFINED_START
) : rInfo
.mnEventId
;
702 uno::Sequence
< uno::Any
> aVbaArgs
;
708 case WORKBOOK_ACTIVATE
:
709 case WORKBOOK_DEACTIVATE
:
713 case WORKBOOK_BEFORECLOSE
:
714 case WORKBOOK_BEFOREPRINT
:
715 aVbaArgs
.realloc( 1 );
716 // current cancel state will be inserted by caller
718 // 2 args: saveAs, cancel
719 case WORKBOOK_BEFORESAVE
:
720 aVbaArgs
.realloc( 2 );
721 checkArgumentType
< bool >( rArgs
, 0 );
722 aVbaArgs
[ 0 ] = rArgs
[ 0 ];
723 // current cancel state will be inserted by caller
726 case WORKBOOK_AFTERSAVE
:
727 aVbaArgs
.realloc( 1 );
728 checkArgumentType
< bool >( rArgs
, 0 );
729 aVbaArgs
[ 0 ] = rArgs
[ 0 ];
732 case WORKBOOK_WINDOWACTIVATE
:
733 case WORKBOOK_WINDOWDEACTIVATE
:
734 case WORKBOOK_WINDOWRESIZE
:
735 aVbaArgs
.realloc( 1 );
736 aVbaArgs
[ 0 ] = createWindow( rArgs
, 0 );
739 case WORKBOOK_NEWSHEET
:
740 aVbaArgs
.realloc( 1 );
741 aVbaArgs
[ 0 ] = createWorksheet( rArgs
, 0 );
747 case WORKSHEET_ACTIVATE
:
748 case WORKSHEET_CALCULATE
:
749 case WORKSHEET_DEACTIVATE
:
752 case WORKSHEET_CHANGE
:
753 case WORKSHEET_SELECTIONCHANGE
:
754 aVbaArgs
.realloc( 1 );
755 aVbaArgs
[ 0 ] = createRange( rArgs
, 0 );
757 // 2 args: range, cancel
758 case WORKSHEET_BEFOREDOUBLECLICK
:
759 case WORKSHEET_BEFORERIGHTCLICK
:
760 aVbaArgs
.realloc( 2 );
761 aVbaArgs
[ 0 ] = createRange( rArgs
, 0 );
762 // current cancel state will be inserted by caller
765 case WORKSHEET_FOLLOWHYPERLINK
:
766 aVbaArgs
.realloc( 1 );
767 aVbaArgs
[ 0 ] = createHyperlink( rArgs
, 0 );
771 /* For workbook events associated to sheet events, the workbook event gets
772 the same arguments but with a Worksheet object in front of them. */
773 if( bSheetEventAsBookEvent
)
775 sal_Int32 nLength
= aVbaArgs
.getLength();
776 uno::Sequence
< uno::Any
> aVbaArgs2( nLength
+ 1 );
777 aVbaArgs2
[ 0 ] = createWorksheet( rArgs
, 0 );
778 for( sal_Int32 nIndex
= 0; nIndex
< nLength
; ++nIndex
)
779 aVbaArgs2
[ nIndex
+ 1 ] = aVbaArgs
[ nIndex
];
780 aVbaArgs
= aVbaArgs2
;
786 void ScVbaEventsHelper::implPostProcessEvent( EventQueue
& rEventQueue
,
787 const EventHandlerInfo
& rInfo
, bool bCancel
) throw (uno::RuntimeException
)
789 switch( rInfo
.mnEventId
)
793 // register the listeners
794 if( !mxListener
.is() )
795 mxListener
= new ScVbaEventListener( *this, mxModel
, mpDocShell
);
797 case WORKBOOK_BEFORECLOSE
:
798 /* Execute Auto_Close only if not cancelled by event handler, but
799 before UI asks user whether to cancel closing the document. */
801 rEventQueue
.push_back( AUTO_CLOSE
);
806 OUString
ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo
& rInfo
,
807 const uno::Sequence
< uno::Any
>& rArgs
) const
808 throw (lang::IllegalArgumentException
, uno::RuntimeException
)
810 bool bSheetEvent
= false;
811 rInfo
.maUserData
>>= bSheetEvent
;
812 SCTAB nTab
= bSheetEvent
? lclGetTabFromArgs( rArgs
, 0 ) : -1;
813 if( bSheetEvent
&& (nTab
< 0) )
814 throw lang::IllegalArgumentException();
818 mpDoc
->GetCodeName( nTab
, aCodeName
);
820 aCodeName
= mpDoc
->GetCodeName();
824 // private --------------------------------------------------------------------
828 /** Compares the passed range lists representing sheet selections. Ignores
829 selections that refer to different sheets (returns false in this case). */
830 bool lclSelectionChanged( const ScRangeList
& rLeft
, const ScRangeList
& rRight
)
832 // one of the range lists empty? -> return false, if both lists empty
833 bool bLeftEmpty
= rLeft
.empty();
834 bool bRightEmpty
= rRight
.empty();
835 if( bLeftEmpty
|| bRightEmpty
)
836 return !(bLeftEmpty
&& bRightEmpty
);
838 // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
839 if (rLeft
[0]->aStart
.Tab() != rRight
[0]->aStart
.Tab())
842 // compare all ranges
843 return rLeft
!= rRight
;
848 bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) throw (lang::IllegalArgumentException
, uno::RuntimeException
)
850 uno::Reference
< uno::XInterface
> xOldSelection( maOldSelection
, uno::UNO_QUERY
);
851 uno::Reference
< uno::XInterface
> xNewSelection
= getXSomethingFromArgs
< uno::XInterface
>( rArgs
, nIndex
, false );
852 ScCellRangesBase
* pOldCellRanges
= ScCellRangesBase::getImplementation( xOldSelection
);
853 ScCellRangesBase
* pNewCellRanges
= ScCellRangesBase::getImplementation( xNewSelection
);
854 bool bChanged
= !pOldCellRanges
|| !pNewCellRanges
|| lclSelectionChanged( pOldCellRanges
->GetRangeList(), pNewCellRanges
->GetRangeList() );
855 maOldSelection
<<= xNewSelection
;
859 uno::Any
ScVbaEventsHelper::createWorksheet( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
860 throw (lang::IllegalArgumentException
, uno::RuntimeException
)
862 // extract sheet index, will throw, if parameter is invalid
863 SCTAB nTab
= lclGetTabFromArgs( rArgs
, nIndex
);
864 return uno::Any( excel::getUnoSheetModuleObj( mxModel
, nTab
) );
867 uno::Any
ScVbaEventsHelper::createRange( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
868 throw (lang::IllegalArgumentException
, uno::RuntimeException
)
870 // it is possible to pass an existing VBA Range object
871 uno::Reference
< excel::XRange
> xVbaRange
= getXSomethingFromArgs
< excel::XRange
>( rArgs
, nIndex
);
872 if( !xVbaRange
.is() )
874 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges
= getXSomethingFromArgs
< sheet::XSheetCellRangeContainer
>( rArgs
, nIndex
);
875 uno::Reference
< table::XCellRange
> xRange
= getXSomethingFromArgs
< table::XCellRange
>( rArgs
, nIndex
);
876 if ( !xRanges
.is() && !xRange
.is() )
877 throw lang::IllegalArgumentException();
879 uno::Sequence
< uno::Any
> aArgs( 2 );
882 aArgs
[ 0 ] <<= excel::getUnoSheetModuleObj( xRanges
);
883 aArgs
[ 1 ] <<= xRanges
;
887 aArgs
[ 0 ] <<= excel::getUnoSheetModuleObj( xRange
);
888 aArgs
[ 1 ] <<= xRange
;
890 xVbaRange
.set( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Range", aArgs
), uno::UNO_QUERY_THROW
);
892 return uno::Any( xVbaRange
);
895 uno::Any
ScVbaEventsHelper::createHyperlink( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
896 throw (lang::IllegalArgumentException
, uno::RuntimeException
)
898 uno::Reference
< table::XCell
> xCell
= getXSomethingFromArgs
< table::XCell
>( rArgs
, nIndex
, false );
899 uno::Sequence
< uno::Any
> aArgs( 2 );
900 aArgs
[ 0 ] <<= excel::getUnoSheetModuleObj( xCell
);
901 aArgs
[ 1 ] <<= xCell
;
902 uno::Reference
< uno::XInterface
> xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Hyperlink", aArgs
), uno::UNO_SET_THROW
);
903 return uno::Any( xHyperlink
);
906 uno::Any
ScVbaEventsHelper::createWindow( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
907 throw (lang::IllegalArgumentException
, uno::RuntimeException
)
909 uno::Sequence
< uno::Any
> aArgs( 3 );
910 aArgs
[ 0 ] <<= getVBADocument( mxModel
);
911 aArgs
[ 1 ] <<= mxModel
;
912 aArgs
[ 2 ] <<= getXSomethingFromArgs
< frame::XController
>( rArgs
, nIndex
, false );
913 uno::Reference
< uno::XInterface
> xWindow( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Window", aArgs
), uno::UNO_SET_THROW
);
914 return uno::Any( xWindow
);
917 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
918 ScVbaEventsHelper_get_implementation(
919 css::uno::XComponentContext
*context
,
920 css::uno::Sequence
<css::uno::Any
> const &arguments
)
922 return cppu::acquire(new ScVbaEventsHelper(arguments
, context
));
925 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */