nss: upgrade to release 3.73
[LibreOffice.git] / sc / source / ui / vba / vbaeventshelper.cxx
blob7f70942dadae255571f59bdf28135211e33ed1c0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
44 #include <docsh.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;
54 namespace {
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
67 sal_Int32 nTab = -1;
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 );
77 if( xVbaRange.is() )
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 );
93 if( xRanges.is() )
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& )
114 return nullptr;
117 } // namespace
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 >
125 public:
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;
142 // XWindowListener
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;
151 // XChangesListener
152 virtual void SAL_CALL changesOccurred( const util::ChangesEvent& rEvent ) override;
154 // XEventListener
155 virtual void SAL_CALL disposing( const lang::EventObject& rEvent ) override;
157 private:
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 );
173 private:
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.
185 bool mbDisposed;
188 ScVbaEventListener::ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell ) :
189 mrVbaEvents( rVbaEvents ),
190 mxModel( rxModel ),
191 mpDocShell( pDocShell ),
192 mpActiveWindow( nullptr ),
193 mbWindowResized( false ),
194 mbBorderChanged( false ),
195 mbDisposed( !rxModel.is() )
197 if( !mxModel.is() )
198 return;
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 );
216 if( xWindow.is() )
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 );
238 if( xWindow.is() )
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 );
281 if( mbDisposed )
282 return;
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
290 if( mpActiveWindow )
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 );
302 if( !mbDisposed )
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) )
357 return;
359 util::ElementChange aChange = rEvent.Changes[ 0 ];
360 OUString sOperation;
361 aChange.Accessor >>= sOperation;
362 if( !sOperation.equalsIgnoreAsciiCase("cell-change") )
363 return;
365 if( nCount == 1 )
367 uno::Reference< table::XCellRange > xRangeObj;
368 aChange.ReplacedElement >>= xRangeObj;
369 if( xRangeObj.is() )
371 uno::Sequence< uno::Any > aArgs( 1 );
372 aArgs[0] <<= xRangeObj;
373 mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
375 return;
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() )
389 ScRange aRange;
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 );
410 if( xModel.is() )
412 OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
413 stopModelListening();
414 mbDisposed = true;
415 return;
418 uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
419 if( xController.is() )
421 stopControllerListening( xController );
422 return;
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);
518 release();
521 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs ) :
522 VbaEventsHelperBase( rArgs ),
523 mbOpened( false )
525 mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
526 mpDoc = mpDocShell ? &mpDocShell->GetDocument() : nullptr;
528 if( !mxModel.is() || !mpDocShell || !mpDoc )
529 return;
531 // global
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");
537 // Workbook
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 );
596 aArgs[ 0 ] <<= true;
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
657 if( bExecuteEvent )
658 bExecuteEvent = (rInfo.mnEventId == WORKBOOK_OPEN) ? !mbOpened : mbOpened;
660 // special handling for some events
661 if( bExecuteEvent ) switch( rInfo.mnEventId )
663 case WORKBOOK_OPEN:
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();
674 break;
675 case WORKSHEET_SELECTIONCHANGE:
676 // if selection is not changed, then do not fire the event
677 bExecuteEvent = isSelectionChanged( rArgs, 0 );
678 break;
681 if( bExecuteEvent )
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;
700 switch( nEventId )
702 // *** Workbook ***
704 // no arguments
705 case WORKBOOK_ACTIVATE:
706 case WORKBOOK_DEACTIVATE:
707 case WORKBOOK_OPEN:
708 break;
709 // 1 arg: cancel
710 case WORKBOOK_BEFORECLOSE:
711 case WORKBOOK_BEFOREPRINT:
712 aVbaArgs.realloc( 1 );
713 // current cancel state will be inserted by caller
714 break;
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
721 break;
722 // 1 arg: success
723 case WORKBOOK_AFTERSAVE:
724 aVbaArgs.realloc( 1 );
725 checkArgumentType< bool >( rArgs, 0 );
726 aVbaArgs[ 0 ] = rArgs[ 0 ];
727 break;
728 // 1 arg: window
729 case WORKBOOK_WINDOWACTIVATE:
730 case WORKBOOK_WINDOWDEACTIVATE:
731 case WORKBOOK_WINDOWRESIZE:
732 aVbaArgs.realloc( 1 );
733 aVbaArgs[ 0 ] = createWindow( rArgs, 0 );
734 break;
735 // 1 arg: worksheet
736 case WORKBOOK_NEWSHEET:
737 aVbaArgs.realloc( 1 );
738 aVbaArgs[ 0 ] = createWorksheet( rArgs, 0 );
739 break;
741 // *** Worksheet ***
743 // no arguments
744 case WORKSHEET_ACTIVATE:
745 case WORKSHEET_CALCULATE:
746 case WORKSHEET_DEACTIVATE:
747 break;
748 // 1 arg: range
749 case WORKSHEET_CHANGE:
750 case WORKSHEET_SELECTIONCHANGE:
751 aVbaArgs.realloc( 1 );
752 aVbaArgs[ 0 ] = createRange( rArgs, 0 );
753 break;
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
760 break;
761 // 1 arg: hyperlink
762 case WORKSHEET_FOLLOWHYPERLINK:
763 aVbaArgs.realloc( 1 );
764 aVbaArgs[ 0 ] = createHyperlink( rArgs, 0 );
765 break;
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;
779 return aVbaArgs;
782 void ScVbaEventsHelper::implPostProcessEvent( EventQueue& rEventQueue,
783 const EventHandlerInfo& rInfo, bool bCancel )
785 switch( rInfo.mnEventId )
787 case WORKBOOK_OPEN:
788 mbOpened = true;
789 // register the listeners
790 if( !mxListener.is() )
791 mxListener = new ScVbaEventListener( *this, mxModel, mpDocShell );
792 break;
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. */
796 if( !bCancel )
797 rEventQueue.emplace_back(AUTO_CLOSE );
798 break;
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();
811 OUString aCodeName;
812 if( bSheetEvent )
813 mpDoc->GetCodeName( nTab, aCodeName );
814 else
815 aCodeName = mpDoc->GetCodeName();
816 return aCodeName;
819 // private --------------------------------------------------------------------
821 namespace {
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())
835 return false;
837 // compare all ranges
838 return rLeft != rRight;
841 } // namespace
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;
851 return bChanged;
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 );
873 if ( xRanges.is() )
875 aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRanges );
876 aArgs[ 1 ] <<= xRanges;
878 else
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: */