bump product version to 4.1.6.2
[LibreOffice.git] / sc / source / ui / vba / vbaeventshelper.cxx
blob9135324f8dfb8b7e366f8a6c4b5155bb8905ba5b
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"
22 #include <com/sun/star/awt/XTopWindow.hpp>
23 #include <com/sun/star/awt/XTopWindowListener.hpp>
24 #include <com/sun/star/awt/XWindowListener.hpp>
25 #include <com/sun/star/frame/XBorderResizeListener.hpp>
26 #include <com/sun/star/frame/XControllerBorder.hpp>
27 #include <com/sun/star/script/ModuleType.hpp>
28 #include <com/sun/star/script/vba/VBAEventId.hpp>
29 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
30 #include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
31 #include <com/sun/star/table/XCellRange.hpp>
32 #include <com/sun/star/util/XChangesListener.hpp>
33 #include <com/sun/star/util/XChangesNotifier.hpp>
35 #include <cppuhelper/implbase4.hxx>
36 #include <toolkit/unohlp.hxx>
37 #include <unotools/eventcfg.hxx>
38 #include <vbahelper/helperdecl.hxx>
39 #include <vcl/svapp.hxx>
40 #include <vcl/window.hxx>
42 #include "cellsuno.hxx"
43 #include "convuno.hxx"
44 #include "vbaapplication.hxx"
46 using namespace ::com::sun::star;
47 using namespace ::com::sun::star::script::vba::VBAEventId;
48 using namespace ::ooo::vba;
51 // ============================================================================
53 namespace {
55 /** Extracts a sheet index from the specified element of the passed sequence.
56 The element may be an integer, a Calc range or ranges object, or a VBA Range object. */
57 SCTAB lclGetTabFromArgs( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) throw (lang::IllegalArgumentException)
59 VbaEventsHelperBase::checkArgument( rArgs, nIndex );
61 // first try to extract a sheet index
62 sal_Int32 nTab = -1;
63 if( rArgs[ nIndex ] >>= nTab )
65 if( (nTab < 0) || (nTab > MAXTAB) )
66 throw lang::IllegalArgumentException();
67 return static_cast< SCTAB >( nTab );
70 // try VBA Range object
71 uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
72 if( xVbaRange.is() )
74 uno::Reference< XHelperInterface > xVbaHelper( xVbaRange, uno::UNO_QUERY_THROW );
75 // TODO: in the future, the parent may be an excel::XChart (chart sheet) -> will there be a common base interface?
76 uno::Reference< excel::XWorksheet > xVbaSheet( xVbaHelper->getParent(), uno::UNO_QUERY_THROW );
77 // VBA sheet index is 1-based
78 return static_cast< SCTAB >( xVbaSheet->getIndex() - 1 );
81 // try single UNO range object
82 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable = getXSomethingFromArgs< sheet::XCellRangeAddressable >( rArgs, nIndex );
83 if( xCellRangeAddressable.is() )
84 return xCellRangeAddressable->getRangeAddress().Sheet;
86 // at last, try UNO range list
87 uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
88 if( xRanges.is() )
90 uno::Sequence< table::CellRangeAddress > aRangeAddresses = xRanges->getRangeAddresses();
91 if( aRangeAddresses.getLength() > 0 )
92 return aRangeAddresses[ 0 ].Sheet;
95 throw lang::IllegalArgumentException();
98 /** Returns the AWT container window of the passed controller. */
99 uno::Reference< awt::XWindow > lclGetWindowForController( const uno::Reference< frame::XController >& rxController )
101 if( rxController.is() ) try
103 uno::Reference< frame::XFrame > xFrame( rxController->getFrame(), uno::UNO_SET_THROW );
104 return xFrame->getContainerWindow();
106 catch( uno::Exception& )
109 return 0;
112 } // namespace
114 // ============================================================================
116 typedef ::cppu::WeakImplHelper4< awt::XTopWindowListener, awt::XWindowListener, frame::XBorderResizeListener, util::XChangesListener > ScVbaEventListener_BASE;
118 // This class is to process Workbook window related event
119 class ScVbaEventListener : public ScVbaEventListener_BASE
121 public :
122 ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell );
123 virtual ~ScVbaEventListener();
125 /** Starts listening to the passed document controller. */
126 void startControllerListening( const uno::Reference< frame::XController >& rxController );
127 /** Stops listening to the passed document controller. */
128 void stopControllerListening( const uno::Reference< frame::XController >& rxController );
130 // XTopWindowListener
131 virtual void SAL_CALL windowOpened( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
132 virtual void SAL_CALL windowClosing( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
133 virtual void SAL_CALL windowClosed( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
134 virtual void SAL_CALL windowMinimized( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
135 virtual void SAL_CALL windowNormalized( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
136 virtual void SAL_CALL windowActivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
137 virtual void SAL_CALL windowDeactivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
139 // XWindowListener
140 virtual void SAL_CALL windowResized( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException);
141 virtual void SAL_CALL windowMoved( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException);
142 virtual void SAL_CALL windowShown( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
143 virtual void SAL_CALL windowHidden( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
145 // XBorderResizeListener
146 virtual void SAL_CALL borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& aNewSize ) throw (uno::RuntimeException);
148 // XChangesListener
149 virtual void SAL_CALL changesOccurred( const util::ChangesEvent& rEvent ) throw (uno::RuntimeException);
151 // XEventListener
152 virtual void SAL_CALL disposing( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
154 private:
155 /** Starts listening to the document model. */
156 void startModelListening();
157 /** Stops listening to the document model. */
158 void stopModelListening();
160 /** Returns the controller for the passed VCL window. */
161 uno::Reference< frame::XController > getControllerForWindow( Window* pWindow ) const;
163 /** Calls the Workbook_Window[Activate|Deactivate] event handler. */
164 void processWindowActivateEvent( Window* pWindow, bool bActivate );
165 /** Posts a Workbook_WindowResize user event. */
166 void postWindowResizeEvent( Window* pWindow );
167 /** Callback link for Application::PostUserEvent(). */
168 DECL_LINK( processWindowResizeEvent, Window* );
170 private:
171 typedef ::std::map< Window*, uno::Reference< frame::XController > > WindowControllerMap;
173 ::osl::Mutex maMutex;
174 ScVbaEventsHelper& mrVbaEvents;
175 uno::Reference< frame::XModel > mxModel;
176 ScDocShell* mpDocShell;
177 WindowControllerMap maControllers; /// Maps VCL top windows to their controllers.
178 Window* mpActiveWindow; /// Currently activated window, to prevent multiple (de)activation.
179 bool mbWindowResized; /// True = window resize system event processed.
180 bool mbBorderChanged; /// True = borders changed system event processed.
181 bool mbDisposed;
184 // ----------------------------------------------------------------------------
186 ScVbaEventListener::ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell ) :
187 mrVbaEvents( rVbaEvents ),
188 mxModel( rxModel ),
189 mpDocShell( pDocShell ),
190 mpActiveWindow( 0 ),
191 mbWindowResized( false ),
192 mbBorderChanged( false ),
193 mbDisposed( !rxModel.is() )
195 if( !mxModel.is() )
196 return;
198 startModelListening();
201 uno::Reference< frame::XController > xController( mxModel->getCurrentController(), uno::UNO_QUERY_THROW );
202 startControllerListening( xController );
204 catch( uno::Exception& )
209 ScVbaEventListener::~ScVbaEventListener()
213 void ScVbaEventListener::startControllerListening( const uno::Reference< frame::XController >& rxController )
215 ::osl::MutexGuard aGuard( maMutex );
217 uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
218 if( xWindow.is() )
219 try { xWindow->addWindowListener( this ); } catch( uno::Exception& ) {}
221 uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
222 if( xTopWindow.is() )
223 try { xTopWindow->addTopWindowListener( this ); } catch( uno::Exception& ) {}
225 uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
226 if( xControllerBorder.is() )
227 try { xControllerBorder->addBorderResizeListener( this ); } catch( uno::Exception& ) {}
229 if( Window* pWindow = VCLUnoHelper::GetWindow( xWindow ) )
230 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( Window* pWindow = VCLUnoHelper::GetWindow( xWindow ) )
251 maControllers.erase( pWindow );
252 if( pWindow == mpActiveWindow )
253 mpActiveWindow = 0;
257 void SAL_CALL ScVbaEventListener::windowOpened( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
261 void SAL_CALL ScVbaEventListener::windowClosing( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
265 void SAL_CALL ScVbaEventListener::windowClosed( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
269 void SAL_CALL ScVbaEventListener::windowMinimized( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
273 void SAL_CALL ScVbaEventListener::windowNormalized( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
277 void SAL_CALL ScVbaEventListener::windowActivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
279 ::osl::MutexGuard aGuard( maMutex );
281 if( !mbDisposed )
283 uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
284 Window* pWindow = VCLUnoHelper::GetWindow( xWindow );
285 OSL_TRACE( "ScVbaEventListener::windowActivated - pWindow = 0x%x, mpActiveWindow = 0x%x", pWindow, mpActiveWindow );
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;
299 void SAL_CALL ScVbaEventListener::windowDeactivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
301 ::osl::MutexGuard aGuard( maMutex );
303 if( !mbDisposed )
305 uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
306 Window* pWindow = VCLUnoHelper::GetWindow( xWindow );
307 OSL_TRACE( "ScVbaEventListener::windowDeactivated - pWindow = 0x%x, mpActiveWindow = 0x%x", pWindow, mpActiveWindow );
308 // do not fire the deactivation event, if the window is not active (prevent multiple deactivation)
309 if( pWindow && (pWindow == mpActiveWindow) )
310 processWindowActivateEvent( pWindow, false );
311 // forget pointer to the active window
312 mpActiveWindow = 0;
316 void SAL_CALL ScVbaEventListener::windowResized( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException)
318 ::osl::MutexGuard aGuard( maMutex );
320 mbWindowResized = true;
321 if( !mbDisposed && mbBorderChanged )
323 uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
324 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
328 void SAL_CALL ScVbaEventListener::windowMoved( const awt::WindowEvent& /*rEvent*/ ) throw (uno::RuntimeException)
332 void SAL_CALL ScVbaEventListener::windowShown( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
336 void SAL_CALL ScVbaEventListener::windowHidden( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
340 void SAL_CALL ScVbaEventListener::borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& /*aNewSize*/ ) throw (uno::RuntimeException)
342 ::osl::MutexGuard aGuard( maMutex );
344 mbBorderChanged = true;
345 if( !mbDisposed && mbWindowResized )
347 uno::Reference< frame::XController > xController( rSource, uno::UNO_QUERY );
348 uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( xController );
349 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
353 void SAL_CALL ScVbaEventListener::changesOccurred( const util::ChangesEvent& rEvent ) throw (uno::RuntimeException)
355 ::osl::MutexGuard aGuard( maMutex );
357 sal_Int32 nCount = rEvent.Changes.getLength();
358 if( mbDisposed || !mpDocShell || (nCount == 0) )
359 return;
361 util::ElementChange aChange = rEvent.Changes[ 0 ];
362 OUString sOperation;
363 aChange.Accessor >>= sOperation;
364 if( !sOperation.equalsIgnoreAsciiCase("cell-change") )
365 return;
367 if( nCount == 1 )
369 uno::Reference< table::XCellRange > xRangeObj;
370 aChange.ReplacedElement >>= xRangeObj;
371 if( xRangeObj.is() )
373 uno::Sequence< uno::Any > aArgs( 1 );
374 aArgs[0] <<= xRangeObj;
375 mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
377 return;
380 ScRangeList aRangeList;
381 for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
383 aChange = rEvent.Changes[ nIndex ];
384 aChange.Accessor >>= sOperation;
385 uno::Reference< table::XCellRange > xRangeObj;
386 aChange.ReplacedElement >>= xRangeObj;
387 if( xRangeObj.is() && sOperation.equalsIgnoreAsciiCase("cell-change") )
389 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable( xRangeObj, uno::UNO_QUERY );
390 if( xCellRangeAddressable.is() )
392 ScRange aRange;
393 ScUnoConversion::FillScRange( aRange, xCellRangeAddressable->getRangeAddress() );
394 aRangeList.Append( aRange );
399 if (!aRangeList.empty())
401 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( mpDocShell, aRangeList ) );
402 uno::Sequence< uno::Any > aArgs(1);
403 aArgs[0] <<= xRanges;
404 mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
408 void SAL_CALL ScVbaEventListener::disposing( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
410 ::osl::MutexGuard aGuard( maMutex );
412 uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
413 if( xModel.is() )
415 OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
416 stopModelListening();
417 mbDisposed = true;
418 return;
421 uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
422 if( xController.is() )
424 stopControllerListening( xController );
425 return;
429 // private --------------------------------------------------------------------
431 void ScVbaEventListener::startModelListening()
435 uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
436 xChangesNotifier->addChangesListener( this );
438 catch( uno::Exception& )
443 void ScVbaEventListener::stopModelListening()
447 uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
448 xChangesNotifier->removeChangesListener( this );
450 catch( uno::Exception& )
455 uno::Reference< frame::XController > ScVbaEventListener::getControllerForWindow( Window* pWindow ) const
457 WindowControllerMap::const_iterator aIt = maControllers.find( pWindow );
458 return (aIt == maControllers.end()) ? uno::Reference< frame::XController >() : aIt->second;
461 void ScVbaEventListener::processWindowActivateEvent( Window* pWindow, bool bActivate )
463 uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
464 if( xController.is() )
466 uno::Sequence< uno::Any > aArgs( 1 );
467 aArgs[ 0 ] <<= xController;
468 mrVbaEvents.processVbaEventNoThrow( bActivate ? WORKBOOK_WINDOWACTIVATE : WORKBOOK_WINDOWDEACTIVATE, aArgs );
472 void ScVbaEventListener::postWindowResizeEvent( Window* pWindow )
474 // check that the passed window is still alive (it must be registered in maControllers)
475 if( pWindow && (maControllers.count( pWindow ) > 0) )
477 mbWindowResized = mbBorderChanged = false;
478 acquire(); // ensure we don't get deleted before the timer fires
479 Application::PostUserEvent( LINK( this, ScVbaEventListener, processWindowResizeEvent ), pWindow );
483 IMPL_LINK( ScVbaEventListener, processWindowResizeEvent, Window*, EMPTYARG pWindow )
485 ::osl::MutexGuard aGuard( maMutex );
487 /* Check that the passed window is still alive (it must be registered in
488 maControllers). While closing a document, postWindowResizeEvent() may
489 be called on the last window which posts a user event via
490 Application::PostUserEvent to call this event handler. VCL will trigger
491 the handler some time later. Sometimes, the window gets deleted before.
492 This is handled via the disposing() function which removes the window
493 pointer from the member maControllers. Thus, checking whether
494 maControllers contains pWindow ensures that the window is still alive. */
495 if( !mbDisposed && pWindow && (maControllers.count( pWindow ) > 0) )
497 // do not fire event unless all mouse buttons have been released
498 Window::PointerState aPointerState = pWindow->GetPointerState();
499 if( (aPointerState.mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0 )
501 uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
502 if( xController.is() )
504 uno::Sequence< uno::Any > aArgs( 1 );
505 aArgs[ 0 ] <<= xController;
506 // #163419# do not throw exceptions into application core
507 mrVbaEvents.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE, aArgs );
511 release();
512 return 0;
515 // ============================================================================
517 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs, const uno::Reference< uno::XComponentContext >& xContext ) :
518 VbaEventsHelperBase( rArgs, xContext ),
519 mbOpened( false )
521 mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
522 mpDoc = mpDocShell ? mpDocShell->GetDocument() : 0;
524 if( !mxModel.is() || !mpDocShell || !mpDoc )
525 return;
527 #define REGISTER_EVENT( eventid, moduletype, classname, eventname, cancelindex, worksheet ) \
528 registerEventHandler( eventid, moduletype, classname "_" eventname, cancelindex, uno::Any( worksheet ) )
529 #define REGISTER_AUTO_EVENT( eventid, eventname ) \
530 REGISTER_EVENT( AUTO_##eventid, script::ModuleType::NORMAL, "Auto", eventname, -1, false )
531 #define REGISTER_WORKBOOK_EVENT( eventid, eventname, cancelindex ) \
532 REGISTER_EVENT( WORKBOOK_##eventid, script::ModuleType::DOCUMENT, "Workbook", eventname, cancelindex, false )
533 #define REGISTER_WORKSHEET_EVENT( eventid, eventname, cancelindex ) \
534 REGISTER_EVENT( WORKSHEET_##eventid, script::ModuleType::DOCUMENT, "Worksheet", eventname, cancelindex, true ); \
535 REGISTER_EVENT( (USERDEFINED_START + WORKSHEET_##eventid), script::ModuleType::DOCUMENT, "Workbook", "Sheet" eventname, (((cancelindex) >= 0) ? ((cancelindex) + 1) : -1), false )
537 // global
538 REGISTER_AUTO_EVENT( OPEN, "Open" );
539 REGISTER_AUTO_EVENT( CLOSE, "Close" );
541 // Workbook
542 REGISTER_WORKBOOK_EVENT( ACTIVATE, "Activate", -1 );
543 REGISTER_WORKBOOK_EVENT( DEACTIVATE, "Deactivate", -1 );
544 REGISTER_WORKBOOK_EVENT( OPEN, "Open", -1 );
545 REGISTER_WORKBOOK_EVENT( BEFORECLOSE, "BeforeClose", 0 );
546 REGISTER_WORKBOOK_EVENT( BEFOREPRINT, "BeforePrint", 0 );
547 REGISTER_WORKBOOK_EVENT( BEFORESAVE, "BeforeSave", 1 );
548 REGISTER_WORKBOOK_EVENT( AFTERSAVE, "AfterSave", -1 );
549 REGISTER_WORKBOOK_EVENT( NEWSHEET, "NewSheet", -1 );
550 REGISTER_WORKBOOK_EVENT( WINDOWACTIVATE, "WindowActivate", -1 );
551 REGISTER_WORKBOOK_EVENT( WINDOWDEACTIVATE, "WindowDeactivate", -1 );
552 REGISTER_WORKBOOK_EVENT( WINDOWRESIZE, "WindowResize", -1 );
554 // Worksheet events. All events have a corresponding workbook event.
555 REGISTER_WORKSHEET_EVENT( ACTIVATE, "Activate", -1 );
556 REGISTER_WORKSHEET_EVENT( DEACTIVATE, "Deactivate", -1 );
557 REGISTER_WORKSHEET_EVENT( BEFOREDOUBLECLICK, "BeforeDoubleClick", 1 );
558 REGISTER_WORKSHEET_EVENT( BEFORERIGHTCLICK, "BeforeRightClick", 1 );
559 REGISTER_WORKSHEET_EVENT( CALCULATE, "Calculate", -1 );
560 REGISTER_WORKSHEET_EVENT( CHANGE, "Change", -1 );
561 REGISTER_WORKSHEET_EVENT( SELECTIONCHANGE, "SelectionChange", -1 );
562 REGISTER_WORKSHEET_EVENT( FOLLOWHYPERLINK, "FollowHyperlink", -1 );
564 #undef REGISTER_WORKSHEET_EVENT
565 #undef REGISTER_WORKBOOK_EVENT
566 #undef REGISTER_AUTO_EVENT
567 #undef REGISTER_EVENT
570 ScVbaEventsHelper::~ScVbaEventsHelper()
574 void SAL_CALL ScVbaEventsHelper::notifyEvent( const css::document::EventObject& rEvent ) throw (css::uno::RuntimeException)
576 static const uno::Sequence< uno::Any > saEmptyArgs;
577 if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_OPENDOC )) ||
578 (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CREATEDOC )) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
580 processVbaEventNoThrow( WORKBOOK_OPEN, saEmptyArgs );
582 else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_ACTIVATEDOC ) )
584 processVbaEventNoThrow( WORKBOOK_ACTIVATE, saEmptyArgs );
586 else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_DEACTIVATEDOC ) )
588 processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
590 else if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEDOCDONE )) ||
591 (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEASDOCDONE )) ||
592 (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVETODOCDONE )) )
594 uno::Sequence< uno::Any > aArgs( 1 );
595 aArgs[ 0 ] <<= true;
596 processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
598 else if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEDOCFAILED )) ||
599 (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEASDOCFAILED )) ||
600 (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVETODOCFAILED )) )
602 uno::Sequence< uno::Any > aArgs( 1 );
603 aArgs[ 0 ] <<= false;
604 processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
606 else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CLOSEDOC ) )
608 /* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
609 events and stop listening to the model (done in base class). */
610 uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
611 if( xController.is() )
613 uno::Sequence< uno::Any > aArgs( 1 );
614 aArgs[ 0 ] <<= xController;
615 processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE, aArgs );
617 processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
619 else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_VIEWCREATED ) )
621 uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
622 if( mxListener.get() && xController.is() )
623 mxListener->startControllerListening( xController );
625 VbaEventsHelperBase::notifyEvent( rEvent );
628 // protected ------------------------------------------------------------------
630 bool ScVbaEventsHelper::implPrepareEvent( EventQueue& rEventQueue,
631 const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& rArgs ) throw (uno::RuntimeException)
633 // document and document shell are needed during event processing
634 if( !mpShell || !mpDoc )
635 throw uno::RuntimeException();
637 /* For document events: check if events are enabled via the
638 Application.EnableEvents symbol (this is an Excel-only attribute).
639 Check this again for every event, as the event handler may change the
640 state of the EnableEvents symbol. Global events such as AUTO_OPEN and
641 AUTO_CLOSE are always enabled. */
642 bool bExecuteEvent = (rInfo.mnModuleType != script::ModuleType::DOCUMENT) || ScVbaApplication::getDocumentEventsEnabled();
644 // framework and Calc fire a few events before 'OnLoad', ignore them
645 if( bExecuteEvent )
646 bExecuteEvent = (rInfo.mnEventId == WORKBOOK_OPEN) ? !mbOpened : mbOpened;
648 // special handling for some events
649 if( bExecuteEvent ) switch( rInfo.mnEventId )
651 case WORKBOOK_OPEN:
653 // execute delayed Activate event too (see above)
654 rEventQueue.push_back( WORKBOOK_ACTIVATE );
655 uno::Sequence< uno::Any > aArgs( 1 );
656 aArgs[ 0 ] <<= mxModel->getCurrentController();
657 rEventQueue.push_back( EventQueueEntry( WORKBOOK_WINDOWACTIVATE, aArgs ) );
658 rEventQueue.push_back( AUTO_OPEN );
659 // remember initial selection
660 maOldSelection <<= mxModel->getCurrentSelection();
662 break;
663 case WORKSHEET_SELECTIONCHANGE:
664 // if selection is not changed, then do not fire the event
665 bExecuteEvent = isSelectionChanged( rArgs, 0 );
666 break;
669 if( bExecuteEvent )
671 // add workbook event associated to a sheet event
672 bool bSheetEvent = false;
673 if( (rInfo.maUserData >>= bSheetEvent) && bSheetEvent )
674 rEventQueue.push_back( EventQueueEntry( rInfo.mnEventId + USERDEFINED_START, rArgs ) );
677 return bExecuteEvent;
680 uno::Sequence< uno::Any > ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& rInfo,
681 const uno::Sequence< uno::Any >& rArgs ) throw (lang::IllegalArgumentException)
683 // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
684 bool bSheetEventAsBookEvent = rInfo.mnEventId > USERDEFINED_START;
685 sal_Int32 nEventId = bSheetEventAsBookEvent ? (rInfo.mnEventId - USERDEFINED_START) : rInfo.mnEventId;
687 uno::Sequence< uno::Any > aVbaArgs;
688 switch( nEventId )
690 // *** Workbook ***
692 // no arguments
693 case WORKBOOK_ACTIVATE:
694 case WORKBOOK_DEACTIVATE:
695 case WORKBOOK_OPEN:
696 break;
697 // 1 arg: cancel
698 case WORKBOOK_BEFORECLOSE:
699 case WORKBOOK_BEFOREPRINT:
700 aVbaArgs.realloc( 1 );
701 // current cancel state will be inserted by caller
702 break;
703 // 2 args: saveAs, cancel
704 case WORKBOOK_BEFORESAVE:
705 aVbaArgs.realloc( 2 );
706 checkArgumentType< bool >( rArgs, 0 );
707 aVbaArgs[ 0 ] = rArgs[ 0 ];
708 // current cancel state will be inserted by caller
709 break;
710 // 1 arg: success
711 case WORKBOOK_AFTERSAVE:
712 aVbaArgs.realloc( 1 );
713 checkArgumentType< bool >( rArgs, 0 );
714 aVbaArgs[ 0 ] = rArgs[ 0 ];
715 break;
716 // 1 arg: window
717 case WORKBOOK_WINDOWACTIVATE:
718 case WORKBOOK_WINDOWDEACTIVATE:
719 case WORKBOOK_WINDOWRESIZE:
720 aVbaArgs.realloc( 1 );
721 aVbaArgs[ 0 ] = createWindow( rArgs, 0 );
722 break;
723 // 1 arg: worksheet
724 case WORKBOOK_NEWSHEET:
725 aVbaArgs.realloc( 1 );
726 aVbaArgs[ 0 ] = createWorksheet( rArgs, 0 );
727 break;
729 // *** Worksheet ***
731 // no arguments
732 case WORKSHEET_ACTIVATE:
733 case WORKSHEET_CALCULATE:
734 case WORKSHEET_DEACTIVATE:
735 break;
736 // 1 arg: range
737 case WORKSHEET_CHANGE:
738 case WORKSHEET_SELECTIONCHANGE:
739 aVbaArgs.realloc( 1 );
740 aVbaArgs[ 0 ] = createRange( rArgs, 0 );
741 break;
742 // 2 args: range, cancel
743 case WORKSHEET_BEFOREDOUBLECLICK:
744 case WORKSHEET_BEFORERIGHTCLICK:
745 aVbaArgs.realloc( 2 );
746 aVbaArgs[ 0 ] = createRange( rArgs, 0 );
747 // current cancel state will be inserted by caller
748 break;
749 // 1 arg: hyperlink
750 case WORKSHEET_FOLLOWHYPERLINK:
751 aVbaArgs.realloc( 1 );
752 aVbaArgs[ 0 ] = createHyperlink( rArgs, 0 );
753 break;
756 /* For workbook events associated to sheet events, the workbook event gets
757 the same arguments but with a Worksheet object in front of them. */
758 if( bSheetEventAsBookEvent )
760 sal_Int32 nLength = aVbaArgs.getLength();
761 uno::Sequence< uno::Any > aVbaArgs2( nLength + 1 );
762 aVbaArgs2[ 0 ] = createWorksheet( rArgs, 0 );
763 for( sal_Int32 nIndex = 0; nIndex < nLength; ++nIndex )
764 aVbaArgs2[ nIndex + 1 ] = aVbaArgs[ nIndex ];
765 aVbaArgs = aVbaArgs2;
768 return aVbaArgs;
771 void ScVbaEventsHelper::implPostProcessEvent( EventQueue& rEventQueue,
772 const EventHandlerInfo& rInfo, bool bCancel ) throw (uno::RuntimeException)
774 switch( rInfo.mnEventId )
776 case WORKBOOK_OPEN:
777 mbOpened = true;
778 // register the listeners
779 if( !mxListener.is() )
780 mxListener = new ScVbaEventListener( *this, mxModel, mpDocShell );
781 break;
782 case WORKBOOK_BEFORECLOSE:
783 /* Execute Auto_Close only if not cancelled by event handler, but
784 before UI asks user whether to cancel closing the document. */
785 if( !bCancel )
786 rEventQueue.push_back( AUTO_CLOSE );
787 break;
791 OUString ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& rInfo,
792 const uno::Sequence< uno::Any >& rArgs ) const throw (lang::IllegalArgumentException)
794 bool bSheetEvent = false;
795 rInfo.maUserData >>= bSheetEvent;
796 SCTAB nTab = bSheetEvent ? lclGetTabFromArgs( rArgs, 0 ) : -1;
797 if( bSheetEvent && (nTab < 0) )
798 throw lang::IllegalArgumentException();
800 OUString aCodeName;
801 if( bSheetEvent )
802 mpDoc->GetCodeName( nTab, aCodeName );
803 else
804 aCodeName = mpDoc->GetCodeName();
805 return aCodeName;
808 // private --------------------------------------------------------------------
810 namespace {
812 /** Compares the passed range lists representing sheet selections. Ignores
813 selections that refer to different sheets (returns false in this case). */
814 bool lclSelectionChanged( const ScRangeList& rLeft, const ScRangeList& rRight )
816 // one of the range lists empty? -> return false, if both lists empty
817 bool bLeftEmpty = rLeft.empty();
818 bool bRightEmpty = rRight.empty();
819 if( bLeftEmpty || bRightEmpty )
820 return !(bLeftEmpty && bRightEmpty);
822 // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
823 if (rLeft[0]->aStart.Tab() != rRight[0]->aStart.Tab())
824 return false;
826 // compare all ranges
827 return rLeft != rRight;
830 } // namespace
832 bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) throw (lang::IllegalArgumentException, uno::RuntimeException)
834 uno::Reference< uno::XInterface > xOldSelection( maOldSelection, uno::UNO_QUERY );
835 uno::Reference< uno::XInterface > xNewSelection = getXSomethingFromArgs< uno::XInterface >( rArgs, nIndex, false );
836 ScCellRangesBase* pOldCellRanges = ScCellRangesBase::getImplementation( xOldSelection );
837 ScCellRangesBase* pNewCellRanges = ScCellRangesBase::getImplementation( xNewSelection );
838 bool bChanged = !pOldCellRanges || !pNewCellRanges || lclSelectionChanged( pOldCellRanges->GetRangeList(), pNewCellRanges->GetRangeList() );
839 maOldSelection <<= xNewSelection;
840 return bChanged;
843 uno::Any ScVbaEventsHelper::createWorksheet( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
844 throw (lang::IllegalArgumentException, uno::RuntimeException)
846 // extract sheet index, will throw, if parameter is invalid
847 SCTAB nTab = lclGetTabFromArgs( rArgs, nIndex );
848 return uno::Any( excel::getUnoSheetModuleObj( mxModel, nTab ) );
851 uno::Any ScVbaEventsHelper::createRange( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
852 throw (lang::IllegalArgumentException, uno::RuntimeException)
854 // it is possible to pass an existing VBA Range object
855 uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
856 if( !xVbaRange.is() )
858 uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
859 uno::Reference< table::XCellRange > xRange = getXSomethingFromArgs< table::XCellRange >( rArgs, nIndex );
860 if ( !xRanges.is() && !xRange.is() )
861 throw lang::IllegalArgumentException();
863 uno::Sequence< uno::Any > aArgs( 2 );
864 if ( xRanges.is() )
866 aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRanges );
867 aArgs[ 1 ] <<= xRanges;
869 else
871 aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRange );
872 aArgs[ 1 ] <<= xRange;
874 xVbaRange.set( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Range", aArgs ), uno::UNO_QUERY_THROW );
876 return uno::Any( xVbaRange );
879 uno::Any ScVbaEventsHelper::createHyperlink( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
880 throw (lang::IllegalArgumentException, uno::RuntimeException)
882 uno::Reference< table::XCell > xCell = getXSomethingFromArgs< table::XCell >( rArgs, nIndex, false );
883 uno::Sequence< uno::Any > aArgs( 2 );
884 aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xCell );
885 aArgs[ 1 ] <<= xCell;
886 uno::Reference< uno::XInterface > xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Hyperlink", aArgs ), uno::UNO_SET_THROW );
887 return uno::Any( xHyperlink );
890 uno::Any ScVbaEventsHelper::createWindow( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
891 throw (lang::IllegalArgumentException, uno::RuntimeException)
893 uno::Sequence< uno::Any > aArgs( 3 );
894 aArgs[ 0 ] <<= getVBADocument( mxModel );
895 aArgs[ 1 ] <<= mxModel;
896 aArgs[ 2 ] <<= getXSomethingFromArgs< frame::XController >( rArgs, nIndex, false );
897 uno::Reference< uno::XInterface > xWindow( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Window", aArgs ), uno::UNO_SET_THROW );
898 return uno::Any( xWindow );
901 // ============================================================================
903 namespace vbaeventshelper
905 namespace sdecl = comphelper::service_decl;
906 sdecl::class_<ScVbaEventsHelper, sdecl::with_args<true> > serviceImpl;
907 extern sdecl::ServiceDecl const serviceDecl(
908 serviceImpl,
909 "ScVbaEventsHelper",
910 "com.sun.star.script.vba.VBASpreadsheetEventProcessor" );
913 // ============================================================================
915 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */