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"
21 #include "excelvbahelper.hxx"
23 #include <com/sun/star/awt/XTopWindow.hpp>
24 #include <com/sun/star/awt/XTopWindowListener.hpp>
25 #include <com/sun/star/awt/XWindowListener.hpp>
26 #include <com/sun/star/frame/XBorderResizeListener.hpp>
27 #include <com/sun/star/frame/XControllerBorder.hpp>
28 #include <com/sun/star/script/ModuleType.hpp>
29 #include <com/sun/star/script/vba/VBAEventId.hpp>
30 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
31 #include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
32 #include <com/sun/star/table/XCellRange.hpp>
33 #include <com/sun/star/util/XChangesListener.hpp>
34 #include <com/sun/star/util/XChangesNotifier.hpp>
36 #include <cppuhelper/implbase.hxx>
37 #include <toolkit/helper/vclunohelper.hxx>
38 #include <unotools/eventcfg.hxx>
39 #include <vcl/event.hxx>
40 #include <vcl/svapp.hxx>
41 #include <vcl/window.hxx>
42 #include <vbahelper/vbaaccesshelper.hxx>
45 #include <document.hxx>
46 #include <cellsuno.hxx>
47 #include <convuno.hxx>
48 #include "vbaapplication.hxx"
50 using namespace ::com::sun::star
;
51 using namespace ::com::sun::star::script::vba::VBAEventId
;
52 using namespace ::ooo::vba
;
56 /** Extracts a sheet index from the specified element of the passed sequence.
57 The element may be an integer, a Calc range or ranges object, or a VBA Range object.
59 @throws lang::IllegalArgumentException
60 @throws uno::RuntimeException
62 SCTAB
lclGetTabFromArgs( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
)
64 VbaEventsHelperBase::checkArgument( rArgs
, nIndex
);
66 // first try to extract a sheet index
68 if( rArgs
[ nIndex
] >>= nTab
)
70 if( (nTab
< 0) || (nTab
> MAXTAB
) )
71 throw lang::IllegalArgumentException();
72 return static_cast< SCTAB
>( nTab
);
75 // try VBA Range object
76 uno::Reference
< excel::XRange
> xVbaRange
= getXSomethingFromArgs
< excel::XRange
>( rArgs
, nIndex
);
79 uno::Reference
< XHelperInterface
> xVbaHelper( xVbaRange
, uno::UNO_QUERY_THROW
);
80 // TODO: in the future, the parent may be an excel::XChart (chart sheet) -> will there be a common base interface?
81 uno::Reference
< excel::XWorksheet
> xVbaSheet( xVbaHelper
->getParent(), uno::UNO_QUERY_THROW
);
82 // VBA sheet index is 1-based
83 return static_cast< SCTAB
>( xVbaSheet
->getIndex() - 1 );
86 // try single UNO range object
87 uno::Reference
< sheet::XCellRangeAddressable
> xCellRangeAddressable
= getXSomethingFromArgs
< sheet::XCellRangeAddressable
>( rArgs
, nIndex
);
88 if( xCellRangeAddressable
.is() )
89 return xCellRangeAddressable
->getRangeAddress().Sheet
;
91 // at last, try UNO range list
92 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges
= getXSomethingFromArgs
< sheet::XSheetCellRangeContainer
>( rArgs
, nIndex
);
95 uno::Sequence
< table::CellRangeAddress
> aRangeAddresses
= xRanges
->getRangeAddresses();
96 if( aRangeAddresses
.hasElements() )
97 return aRangeAddresses
[ 0 ].Sheet
;
100 throw lang::IllegalArgumentException();
103 /** Returns the AWT container window of the passed controller. */
104 uno::Reference
< awt::XWindow
> lclGetWindowForController( const uno::Reference
< frame::XController
>& rxController
)
106 if( rxController
.is() ) try
108 uno::Reference
< frame::XFrame
> xFrame( rxController
->getFrame(), uno::UNO_SET_THROW
);
109 return xFrame
->getContainerWindow();
111 catch( uno::Exception
& )
119 // This class is to process Workbook window related event
120 class ScVbaEventListener
: public ::cppu::WeakImplHelper
< awt::XTopWindowListener
,
121 awt::XWindowListener
,
122 frame::XBorderResizeListener
,
123 util::XChangesListener
>
126 ScVbaEventListener( ScVbaEventsHelper
& rVbaEvents
, const uno::Reference
< frame::XModel
>& rxModel
, ScDocShell
* pDocShell
);
128 /** Starts listening to the passed document controller. */
129 void startControllerListening( const uno::Reference
< frame::XController
>& rxController
);
130 /** Stops listening to the passed document controller. */
131 void stopControllerListening( const uno::Reference
< frame::XController
>& rxController
);
133 // XTopWindowListener
134 virtual void SAL_CALL
windowOpened( const lang::EventObject
& rEvent
) override
;
135 virtual void SAL_CALL
windowClosing( const lang::EventObject
& rEvent
) override
;
136 virtual void SAL_CALL
windowClosed( const lang::EventObject
& rEvent
) override
;
137 virtual void SAL_CALL
windowMinimized( const lang::EventObject
& rEvent
) override
;
138 virtual void SAL_CALL
windowNormalized( const lang::EventObject
& rEvent
) override
;
139 virtual void SAL_CALL
windowActivated( const lang::EventObject
& rEvent
) override
;
140 virtual void SAL_CALL
windowDeactivated( const lang::EventObject
& rEvent
) override
;
143 virtual void SAL_CALL
windowResized( const awt::WindowEvent
& rEvent
) override
;
144 virtual void SAL_CALL
windowMoved( const awt::WindowEvent
& rEvent
) override
;
145 virtual void SAL_CALL
windowShown( const lang::EventObject
& rEvent
) override
;
146 virtual void SAL_CALL
windowHidden( const lang::EventObject
& rEvent
) override
;
148 // XBorderResizeListener
149 virtual void SAL_CALL
borderWidthsChanged( const uno::Reference
< uno::XInterface
>& rSource
, const frame::BorderWidths
& aNewSize
) override
;
152 virtual void SAL_CALL
changesOccurred( const util::ChangesEvent
& rEvent
) override
;
155 virtual void SAL_CALL
disposing( const lang::EventObject
& rEvent
) override
;
158 /** Starts listening to the document model. */
159 void startModelListening();
160 /** Stops listening to the document model. */
161 void stopModelListening();
163 /** Returns the controller for the passed VCL window. */
164 uno::Reference
< frame::XController
> getControllerForWindow( vcl::Window
* pWindow
) const;
166 /** Calls the Workbook_Window[Activate|Deactivate] event handler. */
167 void processWindowActivateEvent( vcl::Window
* pWindow
, bool bActivate
);
168 /** Posts a Workbook_WindowResize user event. */
169 void postWindowResizeEvent( vcl::Window
* pWindow
);
170 /** Callback link for Application::PostUserEvent(). */
171 DECL_LINK( processWindowResizeEvent
, void*, void );
174 typedef ::std::map
< VclPtr
<vcl::Window
>, uno::Reference
< frame::XController
> > WindowControllerMap
;
176 ::osl::Mutex maMutex
;
177 ScVbaEventsHelper
& mrVbaEvents
;
178 uno::Reference
< frame::XModel
> mxModel
;
179 ScDocShell
* mpDocShell
;
180 WindowControllerMap maControllers
; /// Maps VCL top windows to their controllers.
181 std::multiset
< VclPtr
<vcl::Window
> > m_PostedWindows
; /// Keeps processWindowResizeEvent windows from being deleted between postWindowResizeEvent and processWindowResizeEvent
182 VclPtr
<vcl::Window
> mpActiveWindow
; /// Currently activated window, to prevent multiple (de)activation.
183 bool mbWindowResized
; /// True = window resize system event processed.
184 bool mbBorderChanged
; /// True = borders changed system event processed.
188 ScVbaEventListener::ScVbaEventListener( ScVbaEventsHelper
& rVbaEvents
, const uno::Reference
< frame::XModel
>& rxModel
, ScDocShell
* pDocShell
) :
189 mrVbaEvents( rVbaEvents
),
191 mpDocShell( pDocShell
),
192 mpActiveWindow( nullptr ),
193 mbWindowResized( false ),
194 mbBorderChanged( false ),
195 mbDisposed( !rxModel
.is() )
200 startModelListening();
203 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController(), uno::UNO_SET_THROW
);
204 startControllerListening( xController
);
206 catch( uno::Exception
& )
211 void ScVbaEventListener::startControllerListening( const uno::Reference
< frame::XController
>& rxController
)
213 ::osl::MutexGuard
aGuard( maMutex
);
215 uno::Reference
< awt::XWindow
> xWindow
= lclGetWindowForController( rxController
);
217 try { xWindow
->addWindowListener( this ); } catch( uno::Exception
& ) {}
219 uno::Reference
< awt::XTopWindow
> xTopWindow( xWindow
, uno::UNO_QUERY
);
220 if( xTopWindow
.is() )
221 try { xTopWindow
->addTopWindowListener( this ); } catch( uno::Exception
& ) {}
223 uno::Reference
< frame::XControllerBorder
> xControllerBorder( rxController
, uno::UNO_QUERY
);
224 if( xControllerBorder
.is() )
225 try { xControllerBorder
->addBorderResizeListener( this ); } catch( uno::Exception
& ) {}
227 if( VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow( xWindow
) )
229 maControllers
[ pWindow
] = rxController
;
233 void ScVbaEventListener::stopControllerListening( const uno::Reference
< frame::XController
>& rxController
)
235 ::osl::MutexGuard
aGuard( maMutex
);
237 uno::Reference
< awt::XWindow
> xWindow
= lclGetWindowForController( rxController
);
239 try { xWindow
->removeWindowListener( this ); } catch( uno::Exception
& ) {}
241 uno::Reference
< awt::XTopWindow
> xTopWindow( xWindow
, uno::UNO_QUERY
);
242 if( xTopWindow
.is() )
243 try { xTopWindow
->removeTopWindowListener( this ); } catch( uno::Exception
& ) {}
245 uno::Reference
< frame::XControllerBorder
> xControllerBorder( rxController
, uno::UNO_QUERY
);
246 if( xControllerBorder
.is() )
247 try { xControllerBorder
->removeBorderResizeListener( this ); } catch( uno::Exception
& ) {}
249 if( VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow( xWindow
) )
251 maControllers
.erase( pWindow
);
252 if( pWindow
== mpActiveWindow
)
253 mpActiveWindow
= nullptr;
257 void SAL_CALL
ScVbaEventListener::windowOpened( const lang::EventObject
& /*rEvent*/ )
261 void SAL_CALL
ScVbaEventListener::windowClosing( const lang::EventObject
& /*rEvent*/ )
265 void SAL_CALL
ScVbaEventListener::windowClosed( const lang::EventObject
& /*rEvent*/ )
269 void SAL_CALL
ScVbaEventListener::windowMinimized( const lang::EventObject
& /*rEvent*/ )
273 void SAL_CALL
ScVbaEventListener::windowNormalized( const lang::EventObject
& /*rEvent*/ )
277 void SAL_CALL
ScVbaEventListener::windowActivated( const lang::EventObject
& rEvent
)
279 ::osl::MutexGuard
aGuard( maMutex
);
284 uno::Reference
< awt::XWindow
> xWindow( rEvent
.Source
, uno::UNO_QUERY
);
285 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow( xWindow
);
286 // do not fire activation event multiple time for the same window
287 if( pWindow
&& (pWindow
!= mpActiveWindow
) )
289 // if another window is active, fire deactivation event first
291 processWindowActivateEvent( mpActiveWindow
, false );
292 // fire activation event for the new window
293 processWindowActivateEvent( pWindow
, true );
294 mpActiveWindow
= std::move(pWindow
);
298 void SAL_CALL
ScVbaEventListener::windowDeactivated( const lang::EventObject
& rEvent
)
300 ::osl::MutexGuard
aGuard( maMutex
);
304 uno::Reference
< awt::XWindow
> xWindow( rEvent
.Source
, uno::UNO_QUERY
);
305 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow( xWindow
);
306 // do not fire the deactivation event, if the window is not active (prevent multiple deactivation)
307 if( pWindow
&& (pWindow
== mpActiveWindow
) )
308 processWindowActivateEvent( pWindow
, false );
309 // forget pointer to the active window
310 mpActiveWindow
= nullptr;
314 void SAL_CALL
ScVbaEventListener::windowResized( const awt::WindowEvent
& rEvent
)
316 ::osl::MutexGuard
aGuard( maMutex
);
318 mbWindowResized
= true;
319 if( !mbDisposed
&& mbBorderChanged
)
321 uno::Reference
< awt::XWindow
> xWindow( rEvent
.Source
, uno::UNO_QUERY
);
322 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow
) );
326 void SAL_CALL
ScVbaEventListener::windowMoved( const awt::WindowEvent
& /*rEvent*/ )
330 void SAL_CALL
ScVbaEventListener::windowShown( const lang::EventObject
& /*rEvent*/ )
334 void SAL_CALL
ScVbaEventListener::windowHidden( const lang::EventObject
& /*rEvent*/ )
338 void SAL_CALL
ScVbaEventListener::borderWidthsChanged( const uno::Reference
< uno::XInterface
>& rSource
, const frame::BorderWidths
& /*aNewSize*/ )
340 ::osl::MutexGuard
aGuard( maMutex
);
342 mbBorderChanged
= true;
343 if( !mbDisposed
&& mbWindowResized
)
345 uno::Reference
< frame::XController
> xController( rSource
, uno::UNO_QUERY
);
346 uno::Reference
< awt::XWindow
> xWindow
= lclGetWindowForController( xController
);
347 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow
) );
351 void SAL_CALL
ScVbaEventListener::changesOccurred( const util::ChangesEvent
& rEvent
)
353 ::osl::MutexGuard
aGuard( maMutex
);
355 sal_Int32 nCount
= rEvent
.Changes
.getLength();
356 if( mbDisposed
|| !mpDocShell
|| (nCount
== 0) )
359 util::ElementChange aChange
= rEvent
.Changes
[ 0 ];
361 aChange
.Accessor
>>= sOperation
;
362 if( !sOperation
.equalsIgnoreAsciiCase("cell-change") )
367 uno::Reference
< table::XCellRange
> xRangeObj
;
368 aChange
.ReplacedElement
>>= xRangeObj
;
371 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(xRangeObj
) };
372 mrVbaEvents
.processVbaEventNoThrow( WORKSHEET_CHANGE
, aArgs
);
377 ScRangeList aRangeList
;
378 for( const util::ElementChange
& rChange
: rEvent
.Changes
)
380 rChange
.Accessor
>>= sOperation
;
381 uno::Reference
< table::XCellRange
> xRangeObj
;
382 rChange
.ReplacedElement
>>= xRangeObj
;
383 if( xRangeObj
.is() && sOperation
.equalsIgnoreAsciiCase("cell-change") )
385 uno::Reference
< sheet::XCellRangeAddressable
> xCellRangeAddressable( xRangeObj
, uno::UNO_QUERY
);
386 if( xCellRangeAddressable
.is() )
389 ScUnoConversion::FillScRange( aRange
, xCellRangeAddressable
->getRangeAddress() );
390 aRangeList
.push_back( aRange
);
395 if (!aRangeList
.empty())
397 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges( new ScCellRangesObj( mpDocShell
, aRangeList
) );
398 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(xRanges
) };
399 mrVbaEvents
.processVbaEventNoThrow( WORKSHEET_CHANGE
, aArgs
);
403 void SAL_CALL
ScVbaEventListener::disposing( const lang::EventObject
& rEvent
)
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
{ uno::Any(xController
) };
462 mrVbaEvents
.processVbaEventNoThrow( bActivate
? WORKBOOK_WINDOWACTIVATE
: WORKBOOK_WINDOWDEACTIVATE
, aArgs
);
466 void ScVbaEventListener::postWindowResizeEvent( vcl::Window
* pWindow
)
468 // check that the passed window is still alive (it must be registered in maControllers)
469 if( pWindow
&& (maControllers
.count( pWindow
) > 0) )
471 mbWindowResized
= mbBorderChanged
= false;
472 acquire(); // ensure we don't get deleted before the timer fires
473 m_PostedWindows
.insert(pWindow
);
474 Application::PostUserEvent( LINK( this, ScVbaEventListener
, processWindowResizeEvent
), pWindow
);
478 IMPL_LINK( ScVbaEventListener
, processWindowResizeEvent
, void*, p
, void )
480 vcl::Window
* pWindow
= static_cast<vcl::Window
*>(p
);
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
{ uno::Any(xController
) };
501 // #163419# do not throw exceptions into application core
502 mrVbaEvents
.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE
, aArgs
);
507 // note: there may be multiple processWindowResizeEvent outstanding
508 // for pWindow, so it may have been added to m_PostedWindows multiple
509 // times - so this must delete exactly one of these elements!
510 auto const iter(m_PostedWindows
.find(pWindow
));
511 assert(iter
!= m_PostedWindows
.end());
512 m_PostedWindows
.erase(iter
);
517 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence
< uno::Any
>& rArgs
) :
518 VbaEventsHelperBase( rArgs
),
521 mpDocShell
= dynamic_cast< ScDocShell
* >( mpShell
); // mpShell from base class
522 mpDoc
= mpDocShell
? &mpDocShell
->GetDocument() : nullptr;
524 if( !mxModel
.is() || !mpDocShell
|| !mpDoc
)
528 auto registerAutoEvent
= [this](sal_Int32 nID
, const char* sName
)
529 { registerEventHandler(nID
, script::ModuleType::NORMAL
, OString(OString::Concat("Auto_") + sName
).getStr(), -1, uno::Any(false)); };
530 registerAutoEvent(AUTO_OPEN
, "Open");
531 registerAutoEvent(AUTO_CLOSE
, "Close");
534 auto registerWorkbookEvent
= [this](sal_Int32 nID
, const char* sName
, sal_Int32 nCancelIndex
)
535 { registerEventHandler(nID
, script::ModuleType::DOCUMENT
, OString(OString::Concat("Workbook_") + sName
).getStr(), nCancelIndex
, uno::Any(false)); };
536 registerWorkbookEvent( WORKBOOK_ACTIVATE
, "Activate", -1 );
537 registerWorkbookEvent( WORKBOOK_DEACTIVATE
, "Deactivate", -1 );
538 registerWorkbookEvent( WORKBOOK_OPEN
, "Open", -1 );
539 registerWorkbookEvent( WORKBOOK_BEFORECLOSE
, "BeforeClose", 0 );
540 registerWorkbookEvent( WORKBOOK_BEFOREPRINT
, "BeforePrint", 0 );
541 registerWorkbookEvent( WORKBOOK_BEFORESAVE
, "BeforeSave", 1 );
542 registerWorkbookEvent( WORKBOOK_AFTERSAVE
, "AfterSave", -1 );
543 registerWorkbookEvent( WORKBOOK_NEWSHEET
, "NewSheet", -1 );
544 registerWorkbookEvent( WORKBOOK_WINDOWACTIVATE
, "WindowActivate", -1 );
545 registerWorkbookEvent( WORKBOOK_WINDOWDEACTIVATE
, "WindowDeactivate", -1 );
546 registerWorkbookEvent( WORKBOOK_WINDOWRESIZE
, "WindowResize", -1 );
548 // Worksheet events. All events have a corresponding workbook event.
549 auto registerWorksheetEvent
= [this](sal_Int32 nID
, const char* sName
, sal_Int32 nCancelIndex
)
551 registerEventHandler(nID
, script::ModuleType::DOCUMENT
, OString(OString::Concat("Worksheet_") + sName
).getStr(),
552 nCancelIndex
, uno::Any(true));
553 registerEventHandler(USERDEFINED_START
+ nID
, script::ModuleType::DOCUMENT
,
554 OString(OString::Concat("Workbook_Worksheet") + sName
).getStr(),
555 ((nCancelIndex
>= 0) ? (nCancelIndex
+ 1) : -1), uno::Any(false));
557 registerWorksheetEvent( WORKSHEET_ACTIVATE
, "Activate", -1 );
558 registerWorksheetEvent( WORKSHEET_DEACTIVATE
, "Deactivate", -1 );
559 registerWorksheetEvent( WORKSHEET_BEFOREDOUBLECLICK
, "BeforeDoubleClick", 1 );
560 registerWorksheetEvent( WORKSHEET_BEFORERIGHTCLICK
, "BeforeRightClick", 1 );
561 registerWorksheetEvent( WORKSHEET_CALCULATE
, "Calculate", -1 );
562 registerWorksheetEvent( WORKSHEET_CHANGE
, "Change", -1 );
563 registerWorksheetEvent( WORKSHEET_SELECTIONCHANGE
, "SelectionChange", -1 );
564 registerWorksheetEvent( WORKSHEET_FOLLOWHYPERLINK
, "FollowHyperlink", -1 );
567 ScVbaEventsHelper::~ScVbaEventsHelper()
571 void SAL_CALL
ScVbaEventsHelper::notifyEvent( const css::document::EventObject
& rEvent
)
573 static const uno::Sequence
< uno::Any
> saEmptyArgs
;
574 if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC
)) ||
575 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::CREATEDOC
)) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
577 processVbaEventNoThrow( WORKBOOK_OPEN
, saEmptyArgs
);
579 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC
) )
581 processVbaEventNoThrow( WORKBOOK_ACTIVATE
, saEmptyArgs
);
583 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC
) )
585 processVbaEventNoThrow( WORKBOOK_DEACTIVATE
, saEmptyArgs
);
587 else if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCDONE
)) ||
588 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCDONE
)) ||
589 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCDONE
)) )
591 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(true) };
592 processVbaEventNoThrow( WORKBOOK_AFTERSAVE
, aArgs
);
594 else if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCFAILED
)) ||
595 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCFAILED
)) ||
596 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCFAILED
)) )
598 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(false) };
599 processVbaEventNoThrow( WORKBOOK_AFTERSAVE
, aArgs
);
601 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC
) )
603 /* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
604 events and stop listening to the model (done in base class). */
605 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController() );
606 if( xController
.is() )
608 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(xController
) };
609 processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE
, aArgs
);
611 processVbaEventNoThrow( WORKBOOK_DEACTIVATE
, saEmptyArgs
);
613 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED
) )
615 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController() );
616 if( mxListener
&& xController
.is() )
617 mxListener
->startControllerListening( xController
);
619 VbaEventsHelperBase::notifyEvent( rEvent
);
622 OUString
ScVbaEventsHelper::getImplementationName()
624 return u
"ScVbaEventsHelper"_ustr
;
627 css::uno::Sequence
<OUString
> ScVbaEventsHelper::getSupportedServiceNames()
629 return {u
"com.sun.star.script.vba.VBASpreadsheetEventProcessor"_ustr
};
632 // protected ------------------------------------------------------------------
634 bool ScVbaEventsHelper::implPrepareEvent( EventQueue
& rEventQueue
,
635 const EventHandlerInfo
& rInfo
, const uno::Sequence
< uno::Any
>& rArgs
)
637 // document and document shell are needed during event processing
638 if( !mpShell
|| !mpDoc
)
639 throw uno::RuntimeException();
641 /* For document events: check if events are enabled via the
642 Application.EnableEvents symbol (this is an Excel-only attribute).
643 Check this again for every event, as the event handler may change the
644 state of the EnableEvents symbol. Global events such as AUTO_OPEN and
645 AUTO_CLOSE are always enabled. */
646 bool bExecuteEvent
= (rInfo
.mnModuleType
!= script::ModuleType::DOCUMENT
) || ScVbaApplication::getDocumentEventsEnabled();
648 // framework and Calc fire a few events before 'OnLoad', ignore them
650 bExecuteEvent
= (rInfo
.mnEventId
== WORKBOOK_OPEN
) ? !mbOpened
: mbOpened
;
652 // special handling for some events
653 if( bExecuteEvent
) switch( rInfo
.mnEventId
)
657 // execute delayed Activate event too (see above)
658 rEventQueue
.emplace_back(WORKBOOK_ACTIVATE
);
659 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(mxModel
->getCurrentController()) };
660 rEventQueue
.emplace_back( WORKBOOK_WINDOWACTIVATE
, aArgs
);
661 if (!hasModule(u
"Auto_Open"_ustr
))
662 rEventQueue
.emplace_back(AUTO_OPEN
);
663 // remember initial selection
664 maOldSelection
<<= mxModel
->getCurrentSelection();
667 case WORKSHEET_SELECTIONCHANGE
:
668 // if selection is not changed, then do not fire the event
669 bExecuteEvent
= isSelectionChanged( rArgs
, 0 );
675 // add workbook event associated to a sheet event
676 bool bSheetEvent
= false;
677 if( (rInfo
.maUserData
>>= bSheetEvent
) && bSheetEvent
)
678 rEventQueue
.emplace_back( rInfo
.mnEventId
+ USERDEFINED_START
, rArgs
);
681 return bExecuteEvent
;
684 uno::Sequence
< uno::Any
> ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo
& rInfo
,
685 const uno::Sequence
< uno::Any
>& rArgs
)
687 // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
688 bool bSheetEventAsBookEvent
= rInfo
.mnEventId
> USERDEFINED_START
;
689 sal_Int32 nEventId
= bSheetEventAsBookEvent
? (rInfo
.mnEventId
- USERDEFINED_START
) : rInfo
.mnEventId
;
691 uno::Sequence
< uno::Any
> aVbaArgs
;
697 case WORKBOOK_ACTIVATE
:
698 case WORKBOOK_DEACTIVATE
:
702 case WORKBOOK_BEFORECLOSE
:
703 case WORKBOOK_BEFOREPRINT
:
704 aVbaArgs
.realloc( 1 );
705 // current cancel state will be inserted by caller
707 // 2 args: saveAs, cancel
708 case WORKBOOK_BEFORESAVE
:
709 checkArgumentType
< bool >( rArgs
, 0 );
710 aVbaArgs
= { rArgs
[ 0 ], {} };
711 // current cancel state will be inserted by caller
714 case WORKBOOK_AFTERSAVE
:
715 checkArgumentType
< bool >( rArgs
, 0 );
716 aVbaArgs
= { rArgs
[ 0 ] };
719 case WORKBOOK_WINDOWACTIVATE
:
720 case WORKBOOK_WINDOWDEACTIVATE
:
721 case WORKBOOK_WINDOWRESIZE
:
722 aVbaArgs
= { createWindow( rArgs
, 0 ) };
725 case WORKBOOK_NEWSHEET
:
726 aVbaArgs
= { createWorksheet( rArgs
, 0 ) };
732 case WORKSHEET_ACTIVATE
:
733 case WORKSHEET_CALCULATE
:
734 case WORKSHEET_DEACTIVATE
:
737 case WORKSHEET_CHANGE
:
738 case WORKSHEET_SELECTIONCHANGE
:
739 aVbaArgs
= { createRange( rArgs
, 0 ) };
741 // 2 args: range, cancel
742 case WORKSHEET_BEFOREDOUBLECLICK
:
743 case WORKSHEET_BEFORERIGHTCLICK
:
744 aVbaArgs
= { createRange( rArgs
, 0 ), {} };
745 // current cancel state will be inserted by caller
748 case WORKSHEET_FOLLOWHYPERLINK
:
749 aVbaArgs
= { createHyperlink( rArgs
, 0 ) };
753 /* For workbook events associated to sheet events, the workbook event gets
754 the same arguments but with a Worksheet object in front of them. */
755 if( bSheetEventAsBookEvent
)
757 sal_Int32 nLength
= aVbaArgs
.getLength();
758 uno::Sequence
< uno::Any
> aVbaArgs2( nLength
+ 1 );
759 auto pVbaArgs2
= aVbaArgs2
.getArray();
760 *pVbaArgs2
= createWorksheet( rArgs
, 0 );
761 std::copy_n(std::cbegin(aVbaArgs
), nLength
, std::next(pVbaArgs2
));
762 aVbaArgs
= std::move(aVbaArgs2
);
768 void ScVbaEventsHelper::implPostProcessEvent( EventQueue
& rEventQueue
,
769 const EventHandlerInfo
& rInfo
, bool bCancel
)
771 switch( rInfo
.mnEventId
)
775 // register the listeners
776 if( !mxListener
.is() )
777 mxListener
= new ScVbaEventListener( *this, mxModel
, mpDocShell
);
779 case WORKBOOK_BEFORECLOSE
:
780 /* Execute Auto_Close only if not cancelled by event handler, but
781 before UI asks user whether to cancel closing the document. */
782 if (!bCancel
&& !hasModule(u
"Auto_Close"_ustr
))
783 rEventQueue
.emplace_back(AUTO_CLOSE
);
788 OUString
ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo
& rInfo
,
789 const uno::Sequence
< uno::Any
>& rArgs
) const
791 bool bSheetEvent
= false;
792 rInfo
.maUserData
>>= bSheetEvent
;
793 SCTAB nTab
= bSheetEvent
? lclGetTabFromArgs( rArgs
, 0 ) : -1;
794 if( bSheetEvent
&& (nTab
< 0) )
795 throw lang::IllegalArgumentException();
799 mpDoc
->GetCodeName( nTab
, aCodeName
);
801 aCodeName
= mpDoc
->GetCodeName();
805 // private --------------------------------------------------------------------
809 /** Compares the passed range lists representing sheet selections. Ignores
810 selections that refer to different sheets (returns false in this case). */
811 bool lclSelectionChanged( const ScRangeList
& rLeft
, const ScRangeList
& rRight
)
813 // one of the range lists empty? -> return false, if both lists empty
814 bool bLeftEmpty
= rLeft
.empty();
815 bool bRightEmpty
= rRight
.empty();
816 if( bLeftEmpty
|| bRightEmpty
)
817 return !(bLeftEmpty
&& bRightEmpty
);
819 // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
820 if (rLeft
[0].aStart
.Tab() != rRight
[0].aStart
.Tab())
823 // compare all ranges
824 return rLeft
!= rRight
;
829 bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
)
831 uno::Reference
< uno::XInterface
> xOldSelection( maOldSelection
, uno::UNO_QUERY
);
832 uno::Reference
< uno::XInterface
> xNewSelection
= getXSomethingFromArgs
< uno::XInterface
>( rArgs
, nIndex
, false );
833 ScCellRangesBase
* pOldCellRanges
= dynamic_cast<ScCellRangesBase
*>( xOldSelection
.get() );
834 ScCellRangesBase
* pNewCellRanges
= dynamic_cast<ScCellRangesBase
*>( xNewSelection
.get() );
835 bool bChanged
= !pOldCellRanges
|| !pNewCellRanges
|| lclSelectionChanged( pOldCellRanges
->GetRangeList(), pNewCellRanges
->GetRangeList() );
836 maOldSelection
<<= xNewSelection
;
840 uno::Any
ScVbaEventsHelper::createWorksheet( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
842 // extract sheet index, will throw, if parameter is invalid
843 SCTAB nTab
= lclGetTabFromArgs( rArgs
, nIndex
);
844 return uno::Any( excel::getUnoSheetModuleObj( mxModel
, nTab
) );
847 uno::Any
ScVbaEventsHelper::createRange( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
849 // it is possible to pass an existing VBA Range object
850 uno::Reference
< excel::XRange
> xVbaRange
= getXSomethingFromArgs
< excel::XRange
>( rArgs
, nIndex
);
851 if( !xVbaRange
.is() )
853 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges
= getXSomethingFromArgs
< sheet::XSheetCellRangeContainer
>( rArgs
, nIndex
);
854 uno::Reference
< table::XCellRange
> xRange
= getXSomethingFromArgs
< table::XCellRange
>( rArgs
, nIndex
);
855 if ( !xRanges
.is() && !xRange
.is() )
856 throw lang::IllegalArgumentException();
858 uno::Sequence
< uno::Any
> aArgs
;
861 aArgs
= { uno::Any(excel::getUnoSheetModuleObj( xRanges
)), uno::Any(xRanges
) };
865 aArgs
= { uno::Any(excel::getUnoSheetModuleObj( xRange
)), uno::Any(xRange
) };
867 xVbaRange
.set( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Range", aArgs
), uno::UNO_QUERY_THROW
);
869 return uno::Any( xVbaRange
);
872 uno::Any
ScVbaEventsHelper::createHyperlink( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
874 uno::Reference
< table::XCell
> xCell
= getXSomethingFromArgs
< table::XCell
>( rArgs
, nIndex
, false );
875 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(excel::getUnoSheetModuleObj( xCell
)),
877 uno::Reference
< uno::XInterface
> xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Hyperlink", aArgs
), uno::UNO_SET_THROW
);
878 return uno::Any( xHyperlink
);
881 uno::Any
ScVbaEventsHelper::createWindow( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
883 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(getVBADocument( mxModel
)),
885 uno::Any(getXSomethingFromArgs
< frame::XController
>( rArgs
, nIndex
, false )) };
886 uno::Reference
< uno::XInterface
> xWindow( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Window", aArgs
), uno::UNO_SET_THROW
);
887 return uno::Any( xWindow
);
890 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
891 ScVbaEventsHelper_get_implementation(
892 css::uno::XComponentContext
* /*context*/,
893 css::uno::Sequence
<css::uno::Any
> const &arguments
)
895 return cppu::acquire(new ScVbaEventsHelper(arguments
));
898 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */