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
= 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( 1 );
372 aArgs
[0] <<= xRangeObj
;
373 mrVbaEvents
.processVbaEventNoThrow( WORKSHEET_CHANGE
, aArgs
);
378 ScRangeList aRangeList
;
379 for( const util::ElementChange
& rChange
: rEvent
.Changes
)
381 rChange
.Accessor
>>= sOperation
;
382 uno::Reference
< table::XCellRange
> xRangeObj
;
383 rChange
.ReplacedElement
>>= xRangeObj
;
384 if( xRangeObj
.is() && sOperation
.equalsIgnoreAsciiCase("cell-change") )
386 uno::Reference
< sheet::XCellRangeAddressable
> xCellRangeAddressable( xRangeObj
, uno::UNO_QUERY
);
387 if( xCellRangeAddressable
.is() )
390 ScUnoConversion::FillScRange( aRange
, xCellRangeAddressable
->getRangeAddress() );
391 aRangeList
.push_back( aRange
);
396 if (!aRangeList
.empty())
398 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges( new ScCellRangesObj( mpDocShell
, aRangeList
) );
399 uno::Sequence
< uno::Any
> aArgs(1);
400 aArgs
[0] <<= xRanges
;
401 mrVbaEvents
.processVbaEventNoThrow( WORKSHEET_CHANGE
, aArgs
);
405 void SAL_CALL
ScVbaEventListener::disposing( const lang::EventObject
& rEvent
)
407 ::osl::MutexGuard
aGuard( maMutex
);
409 uno::Reference
< frame::XModel
> xModel( rEvent
.Source
, uno::UNO_QUERY
);
412 OSL_ENSURE( xModel
.get() == mxModel
.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
413 stopModelListening();
418 uno::Reference
< frame::XController
> xController( rEvent
.Source
, uno::UNO_QUERY
);
419 if( xController
.is() )
421 stopControllerListening( xController
);
426 // private --------------------------------------------------------------------
428 void ScVbaEventListener::startModelListening()
432 uno::Reference
< util::XChangesNotifier
> xChangesNotifier( mxModel
, uno::UNO_QUERY_THROW
);
433 xChangesNotifier
->addChangesListener( this );
435 catch( uno::Exception
& )
440 void ScVbaEventListener::stopModelListening()
444 uno::Reference
< util::XChangesNotifier
> xChangesNotifier( mxModel
, uno::UNO_QUERY_THROW
);
445 xChangesNotifier
->removeChangesListener( this );
447 catch( uno::Exception
& )
452 uno::Reference
< frame::XController
> ScVbaEventListener::getControllerForWindow( vcl::Window
* pWindow
) const
454 WindowControllerMap::const_iterator aIt
= maControllers
.find( pWindow
);
455 return (aIt
== maControllers
.end()) ? uno::Reference
< frame::XController
>() : aIt
->second
;
458 void ScVbaEventListener::processWindowActivateEvent( vcl::Window
* pWindow
, bool bActivate
)
460 uno::Reference
< frame::XController
> xController
= getControllerForWindow( pWindow
);
461 if( xController
.is() )
463 uno::Sequence
< uno::Any
> aArgs( 1 );
464 aArgs
[ 0 ] <<= xController
;
465 mrVbaEvents
.processVbaEventNoThrow( bActivate
? WORKBOOK_WINDOWACTIVATE
: WORKBOOK_WINDOWDEACTIVATE
, aArgs
);
469 void ScVbaEventListener::postWindowResizeEvent( vcl::Window
* pWindow
)
471 // check that the passed window is still alive (it must be registered in maControllers)
472 if( pWindow
&& (maControllers
.count( pWindow
) > 0) )
474 mbWindowResized
= mbBorderChanged
= false;
475 acquire(); // ensure we don't get deleted before the timer fires
476 m_PostedWindows
.insert(pWindow
);
477 Application::PostUserEvent( LINK( this, ScVbaEventListener
, processWindowResizeEvent
), pWindow
);
481 IMPL_LINK( ScVbaEventListener
, processWindowResizeEvent
, void*, p
, void )
483 vcl::Window
* pWindow
= static_cast<vcl::Window
*>(p
);
484 ::osl::MutexGuard
aGuard( maMutex
);
486 /* Check that the passed window is still alive (it must be registered in
487 maControllers). While closing a document, postWindowResizeEvent() may
488 be called on the last window which posts a user event via
489 Application::PostUserEvent to call this event handler. VCL will trigger
490 the handler some time later. Sometimes, the window gets deleted before.
491 This is handled via the disposing() function which removes the window
492 pointer from the member maControllers. Thus, checking whether
493 maControllers contains pWindow ensures that the window is still alive. */
494 if( !mbDisposed
&& pWindow
&& !pWindow
->IsDisposed() && (maControllers
.count(pWindow
) > 0) )
496 // do not fire event unless all mouse buttons have been released
497 vcl::Window::PointerState aPointerState
= pWindow
->GetPointerState();
498 if( (aPointerState
.mnState
& (MOUSE_LEFT
| MOUSE_MIDDLE
| MOUSE_RIGHT
)) == 0 )
500 uno::Reference
< frame::XController
> xController
= getControllerForWindow( pWindow
);
501 if( xController
.is() )
503 uno::Sequence
< uno::Any
> aArgs( 1 );
504 aArgs
[ 0 ] <<= xController
;
505 // #163419# do not throw exceptions into application core
506 mrVbaEvents
.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE
, aArgs
);
511 // note: there may be multiple processWindowResizeEvent outstanding
512 // for pWindow, so it may have been added to m_PostedWindows multiple
513 // times - so this must delete exactly one of these elements!
514 auto const iter(m_PostedWindows
.find(pWindow
));
515 assert(iter
!= m_PostedWindows
.end());
516 m_PostedWindows
.erase(iter
);
521 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence
< uno::Any
>& rArgs
) :
522 VbaEventsHelperBase( rArgs
),
525 mpDocShell
= dynamic_cast< ScDocShell
* >( mpShell
); // mpShell from base class
526 mpDoc
= mpDocShell
? &mpDocShell
->GetDocument() : nullptr;
528 if( !mxModel
.is() || !mpDocShell
|| !mpDoc
)
532 auto registerAutoEvent
= [this](sal_Int32 nID
, const char* sName
)
533 { registerEventHandler(nID
, script::ModuleType::NORMAL
, OString(OStringLiteral("Auto_") + sName
).getStr(), -1, uno::Any(false)); };
534 registerAutoEvent(AUTO_OPEN
, "Open");
535 registerAutoEvent(AUTO_CLOSE
, "Close");
538 auto registerWorkbookEvent
= [this](sal_Int32 nID
, const char* sName
, sal_Int32 nCancelIndex
)
539 { registerEventHandler(nID
, script::ModuleType::DOCUMENT
, OString(OStringLiteral("Workbook_") + sName
).getStr(), nCancelIndex
, uno::Any(false)); };
540 registerWorkbookEvent( WORKBOOK_ACTIVATE
, "Activate", -1 );
541 registerWorkbookEvent( WORKBOOK_DEACTIVATE
, "Deactivate", -1 );
542 registerWorkbookEvent( WORKBOOK_OPEN
, "Open", -1 );
543 registerWorkbookEvent( WORKBOOK_BEFORECLOSE
, "BeforeClose", 0 );
544 registerWorkbookEvent( WORKBOOK_BEFOREPRINT
, "BeforePrint", 0 );
545 registerWorkbookEvent( WORKBOOK_BEFORESAVE
, "BeforeSave", 1 );
546 registerWorkbookEvent( WORKBOOK_AFTERSAVE
, "AfterSave", -1 );
547 registerWorkbookEvent( WORKBOOK_NEWSHEET
, "NewSheet", -1 );
548 registerWorkbookEvent( WORKBOOK_WINDOWACTIVATE
, "WindowActivate", -1 );
549 registerWorkbookEvent( WORKBOOK_WINDOWDEACTIVATE
, "WindowDeactivate", -1 );
550 registerWorkbookEvent( WORKBOOK_WINDOWRESIZE
, "WindowResize", -1 );
552 // Worksheet events. All events have a corresponding workbook event.
553 auto registerWorksheetEvent
= [this](sal_Int32 nID
, const char* sName
, sal_Int32 nCancelIndex
)
555 registerEventHandler(nID
, script::ModuleType::DOCUMENT
, OString(OStringLiteral("Worksheet_") + sName
).getStr(),
556 nCancelIndex
, uno::Any(true));
557 registerEventHandler(USERDEFINED_START
+ nID
, script::ModuleType::DOCUMENT
,
558 OString(OStringLiteral("Workbook_Worksheet") + sName
).getStr(),
559 ((nCancelIndex
>= 0) ? (nCancelIndex
+ 1) : -1), uno::Any(false));
561 registerWorksheetEvent( WORKSHEET_ACTIVATE
, "Activate", -1 );
562 registerWorksheetEvent( WORKSHEET_DEACTIVATE
, "Deactivate", -1 );
563 registerWorksheetEvent( WORKSHEET_BEFOREDOUBLECLICK
, "BeforeDoubleClick", 1 );
564 registerWorksheetEvent( WORKSHEET_BEFORERIGHTCLICK
, "BeforeRightClick", 1 );
565 registerWorksheetEvent( WORKSHEET_CALCULATE
, "Calculate", -1 );
566 registerWorksheetEvent( WORKSHEET_CHANGE
, "Change", -1 );
567 registerWorksheetEvent( WORKSHEET_SELECTIONCHANGE
, "SelectionChange", -1 );
568 registerWorksheetEvent( WORKSHEET_FOLLOWHYPERLINK
, "FollowHyperlink", -1 );
571 ScVbaEventsHelper::~ScVbaEventsHelper()
575 void SAL_CALL
ScVbaEventsHelper::notifyEvent( const css::document::EventObject
& rEvent
)
577 static const uno::Sequence
< uno::Any
> saEmptyArgs
;
578 if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC
)) ||
579 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::CREATEDOC
)) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
581 processVbaEventNoThrow( WORKBOOK_OPEN
, saEmptyArgs
);
583 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC
) )
585 processVbaEventNoThrow( WORKBOOK_ACTIVATE
, saEmptyArgs
);
587 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC
) )
589 processVbaEventNoThrow( WORKBOOK_DEACTIVATE
, saEmptyArgs
);
591 else if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCDONE
)) ||
592 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCDONE
)) ||
593 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCDONE
)) )
595 uno::Sequence
< uno::Any
> aArgs( 1 );
597 processVbaEventNoThrow( WORKBOOK_AFTERSAVE
, aArgs
);
599 else if( (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCFAILED
)) ||
600 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCFAILED
)) ||
601 (rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCFAILED
)) )
603 uno::Sequence
< uno::Any
> aArgs( 1 );
604 aArgs
[ 0 ] <<= false;
605 processVbaEventNoThrow( WORKBOOK_AFTERSAVE
, aArgs
);
607 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC
) )
609 /* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
610 events and stop listening to the model (done in base class). */
611 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController() );
612 if( xController
.is() )
614 uno::Sequence
< uno::Any
> aArgs( 1 );
615 aArgs
[ 0 ] <<= xController
;
616 processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE
, aArgs
);
618 processVbaEventNoThrow( WORKBOOK_DEACTIVATE
, saEmptyArgs
);
620 else if( rEvent
.EventName
== GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED
) )
622 uno::Reference
< frame::XController
> xController( mxModel
->getCurrentController() );
623 if( mxListener
&& xController
.is() )
624 mxListener
->startControllerListening( xController
);
626 VbaEventsHelperBase::notifyEvent( rEvent
);
629 OUString
ScVbaEventsHelper::getImplementationName()
631 return "ScVbaEventsHelper";
634 css::uno::Sequence
<OUString
> ScVbaEventsHelper::getSupportedServiceNames()
636 return css::uno::Sequence
<OUString
>{
637 "com.sun.star.script.vba.VBASpreadsheetEventProcessor"};
640 // protected ------------------------------------------------------------------
642 bool ScVbaEventsHelper::implPrepareEvent( EventQueue
& rEventQueue
,
643 const EventHandlerInfo
& rInfo
, const uno::Sequence
< uno::Any
>& rArgs
)
645 // document and document shell are needed during event processing
646 if( !mpShell
|| !mpDoc
)
647 throw uno::RuntimeException();
649 /* For document events: check if events are enabled via the
650 Application.EnableEvents symbol (this is an Excel-only attribute).
651 Check this again for every event, as the event handler may change the
652 state of the EnableEvents symbol. Global events such as AUTO_OPEN and
653 AUTO_CLOSE are always enabled. */
654 bool bExecuteEvent
= (rInfo
.mnModuleType
!= script::ModuleType::DOCUMENT
) || ScVbaApplication::getDocumentEventsEnabled();
656 // framework and Calc fire a few events before 'OnLoad', ignore them
658 bExecuteEvent
= (rInfo
.mnEventId
== WORKBOOK_OPEN
) ? !mbOpened
: mbOpened
;
660 // special handling for some events
661 if( bExecuteEvent
) switch( rInfo
.mnEventId
)
665 // execute delayed Activate event too (see above)
666 rEventQueue
.emplace_back(WORKBOOK_ACTIVATE
);
667 uno::Sequence
< uno::Any
> aArgs( 1 );
668 aArgs
[ 0 ] <<= mxModel
->getCurrentController();
669 rEventQueue
.emplace_back( WORKBOOK_WINDOWACTIVATE
, aArgs
);
670 rEventQueue
.emplace_back(AUTO_OPEN
);
671 // remember initial selection
672 maOldSelection
<<= mxModel
->getCurrentSelection();
675 case WORKSHEET_SELECTIONCHANGE
:
676 // if selection is not changed, then do not fire the event
677 bExecuteEvent
= isSelectionChanged( rArgs
, 0 );
683 // add workbook event associated to a sheet event
684 bool bSheetEvent
= false;
685 if( (rInfo
.maUserData
>>= bSheetEvent
) && bSheetEvent
)
686 rEventQueue
.emplace_back( rInfo
.mnEventId
+ USERDEFINED_START
, rArgs
);
689 return bExecuteEvent
;
692 uno::Sequence
< uno::Any
> ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo
& rInfo
,
693 const uno::Sequence
< uno::Any
>& rArgs
)
695 // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
696 bool bSheetEventAsBookEvent
= rInfo
.mnEventId
> USERDEFINED_START
;
697 sal_Int32 nEventId
= bSheetEventAsBookEvent
? (rInfo
.mnEventId
- USERDEFINED_START
) : rInfo
.mnEventId
;
699 uno::Sequence
< uno::Any
> aVbaArgs
;
705 case WORKBOOK_ACTIVATE
:
706 case WORKBOOK_DEACTIVATE
:
710 case WORKBOOK_BEFORECLOSE
:
711 case WORKBOOK_BEFOREPRINT
:
712 aVbaArgs
.realloc( 1 );
713 // current cancel state will be inserted by caller
715 // 2 args: saveAs, cancel
716 case WORKBOOK_BEFORESAVE
:
717 aVbaArgs
.realloc( 2 );
718 checkArgumentType
< bool >( rArgs
, 0 );
719 aVbaArgs
[ 0 ] = rArgs
[ 0 ];
720 // current cancel state will be inserted by caller
723 case WORKBOOK_AFTERSAVE
:
724 aVbaArgs
.realloc( 1 );
725 checkArgumentType
< bool >( rArgs
, 0 );
726 aVbaArgs
[ 0 ] = rArgs
[ 0 ];
729 case WORKBOOK_WINDOWACTIVATE
:
730 case WORKBOOK_WINDOWDEACTIVATE
:
731 case WORKBOOK_WINDOWRESIZE
:
732 aVbaArgs
.realloc( 1 );
733 aVbaArgs
[ 0 ] = createWindow( rArgs
, 0 );
736 case WORKBOOK_NEWSHEET
:
737 aVbaArgs
.realloc( 1 );
738 aVbaArgs
[ 0 ] = createWorksheet( rArgs
, 0 );
744 case WORKSHEET_ACTIVATE
:
745 case WORKSHEET_CALCULATE
:
746 case WORKSHEET_DEACTIVATE
:
749 case WORKSHEET_CHANGE
:
750 case WORKSHEET_SELECTIONCHANGE
:
751 aVbaArgs
.realloc( 1 );
752 aVbaArgs
[ 0 ] = createRange( rArgs
, 0 );
754 // 2 args: range, cancel
755 case WORKSHEET_BEFOREDOUBLECLICK
:
756 case WORKSHEET_BEFORERIGHTCLICK
:
757 aVbaArgs
.realloc( 2 );
758 aVbaArgs
[ 0 ] = createRange( rArgs
, 0 );
759 // current cancel state will be inserted by caller
762 case WORKSHEET_FOLLOWHYPERLINK
:
763 aVbaArgs
.realloc( 1 );
764 aVbaArgs
[ 0 ] = createHyperlink( rArgs
, 0 );
768 /* For workbook events associated to sheet events, the workbook event gets
769 the same arguments but with a Worksheet object in front of them. */
770 if( bSheetEventAsBookEvent
)
772 sal_Int32 nLength
= aVbaArgs
.getLength();
773 uno::Sequence
< uno::Any
> aVbaArgs2( nLength
+ 1 );
774 aVbaArgs2
[ 0 ] = createWorksheet( rArgs
, 0 );
775 std::copy_n(aVbaArgs
.begin(), nLength
, std::next(aVbaArgs2
.begin()));
776 aVbaArgs
= aVbaArgs2
;
782 void ScVbaEventsHelper::implPostProcessEvent( EventQueue
& rEventQueue
,
783 const EventHandlerInfo
& rInfo
, bool bCancel
)
785 switch( rInfo
.mnEventId
)
789 // register the listeners
790 if( !mxListener
.is() )
791 mxListener
= new ScVbaEventListener( *this, mxModel
, mpDocShell
);
793 case WORKBOOK_BEFORECLOSE
:
794 /* Execute Auto_Close only if not cancelled by event handler, but
795 before UI asks user whether to cancel closing the document. */
797 rEventQueue
.emplace_back(AUTO_CLOSE
);
802 OUString
ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo
& rInfo
,
803 const uno::Sequence
< uno::Any
>& rArgs
) const
805 bool bSheetEvent
= false;
806 rInfo
.maUserData
>>= bSheetEvent
;
807 SCTAB nTab
= bSheetEvent
? lclGetTabFromArgs( rArgs
, 0 ) : -1;
808 if( bSheetEvent
&& (nTab
< 0) )
809 throw lang::IllegalArgumentException();
813 mpDoc
->GetCodeName( nTab
, aCodeName
);
815 aCodeName
= mpDoc
->GetCodeName();
819 // private --------------------------------------------------------------------
823 /** Compares the passed range lists representing sheet selections. Ignores
824 selections that refer to different sheets (returns false in this case). */
825 bool lclSelectionChanged( const ScRangeList
& rLeft
, const ScRangeList
& rRight
)
827 // one of the range lists empty? -> return false, if both lists empty
828 bool bLeftEmpty
= rLeft
.empty();
829 bool bRightEmpty
= rRight
.empty();
830 if( bLeftEmpty
|| bRightEmpty
)
831 return !(bLeftEmpty
&& bRightEmpty
);
833 // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
834 if (rLeft
[0].aStart
.Tab() != rRight
[0].aStart
.Tab())
837 // compare all ranges
838 return rLeft
!= rRight
;
843 bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
)
845 uno::Reference
< uno::XInterface
> xOldSelection( maOldSelection
, uno::UNO_QUERY
);
846 uno::Reference
< uno::XInterface
> xNewSelection
= getXSomethingFromArgs
< uno::XInterface
>( rArgs
, nIndex
, false );
847 ScCellRangesBase
* pOldCellRanges
= comphelper::getUnoTunnelImplementation
<ScCellRangesBase
>( xOldSelection
);
848 ScCellRangesBase
* pNewCellRanges
= comphelper::getUnoTunnelImplementation
<ScCellRangesBase
>( xNewSelection
);
849 bool bChanged
= !pOldCellRanges
|| !pNewCellRanges
|| lclSelectionChanged( pOldCellRanges
->GetRangeList(), pNewCellRanges
->GetRangeList() );
850 maOldSelection
<<= xNewSelection
;
854 uno::Any
ScVbaEventsHelper::createWorksheet( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
856 // extract sheet index, will throw, if parameter is invalid
857 SCTAB nTab
= lclGetTabFromArgs( rArgs
, nIndex
);
858 return uno::Any( excel::getUnoSheetModuleObj( mxModel
, nTab
) );
861 uno::Any
ScVbaEventsHelper::createRange( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
863 // it is possible to pass an existing VBA Range object
864 uno::Reference
< excel::XRange
> xVbaRange
= getXSomethingFromArgs
< excel::XRange
>( rArgs
, nIndex
);
865 if( !xVbaRange
.is() )
867 uno::Reference
< sheet::XSheetCellRangeContainer
> xRanges
= getXSomethingFromArgs
< sheet::XSheetCellRangeContainer
>( rArgs
, nIndex
);
868 uno::Reference
< table::XCellRange
> xRange
= getXSomethingFromArgs
< table::XCellRange
>( rArgs
, nIndex
);
869 if ( !xRanges
.is() && !xRange
.is() )
870 throw lang::IllegalArgumentException();
872 uno::Sequence
< uno::Any
> aArgs( 2 );
875 aArgs
[ 0 ] <<= excel::getUnoSheetModuleObj( xRanges
);
876 aArgs
[ 1 ] <<= xRanges
;
880 aArgs
[ 0 ] <<= excel::getUnoSheetModuleObj( xRange
);
881 aArgs
[ 1 ] <<= xRange
;
883 xVbaRange
.set( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Range", aArgs
), uno::UNO_QUERY_THROW
);
885 return uno::Any( xVbaRange
);
888 uno::Any
ScVbaEventsHelper::createHyperlink( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
890 uno::Reference
< table::XCell
> xCell
= getXSomethingFromArgs
< table::XCell
>( rArgs
, nIndex
, false );
891 uno::Sequence
< uno::Any
> aArgs( 2 );
892 aArgs
[ 0 ] <<= excel::getUnoSheetModuleObj( xCell
);
893 aArgs
[ 1 ] <<= xCell
;
894 uno::Reference
< uno::XInterface
> xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Hyperlink", aArgs
), uno::UNO_SET_THROW
);
895 return uno::Any( xHyperlink
);
898 uno::Any
ScVbaEventsHelper::createWindow( const uno::Sequence
< uno::Any
>& rArgs
, sal_Int32 nIndex
) const
900 uno::Sequence
< uno::Any
> aArgs( 3 );
901 aArgs
[ 0 ] <<= getVBADocument( mxModel
);
902 aArgs
[ 1 ] <<= mxModel
;
903 aArgs
[ 2 ] <<= getXSomethingFromArgs
< frame::XController
>( rArgs
, nIndex
, false );
904 uno::Reference
< uno::XInterface
> xWindow( createVBAUnoAPIServiceWithArgs( mpShell
, "ooo.vba.excel.Window", aArgs
), uno::UNO_SET_THROW
);
905 return uno::Any( xWindow
);
908 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
909 ScVbaEventsHelper_get_implementation(
910 css::uno::XComponentContext
* /*context*/,
911 css::uno::Sequence
<css::uno::Any
> const &arguments
)
913 return cppu::acquire(new ScVbaEventsHelper(arguments
));
916 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */