1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include "vbaeventshelper.hxx"
21 #include "excelvbahelper.hxx"
22
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>
35
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>
43
44 #include <docsh.hxx>
45 #include <document.hxx>
46 #include <cellsuno.hxx>
47 #include <convuno.hxx>
48 #include "vbaapplication.hxx"
49
50 using namespace ::com::sun::star;
51 using namespace ::com::sun::star::script::vba::VBAEventId;
52 using namespace ::ooo::vba;
53
54 namespace {
55
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.
58
59 @throws lang::IllegalArgumentException
60 @throws uno::RuntimeException
61 */
lclGetTabFromArgs(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex)62 SCTAB lclGetTabFromArgs( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex )
63 {
64 VbaEventsHelperBase::checkArgument( rArgs, nIndex );
65
66 // first try to extract a sheet index
67 sal_Int32 nTab = -1;
68 if( rArgs[ nIndex ] >>= nTab )
69 {
70 if( (nTab < 0) || (nTab > MAXTAB) )
71 throw lang::IllegalArgumentException();
72 return static_cast< SCTAB >( nTab );
73 }
74
75 // try VBA Range object
76 uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
77 if( xVbaRange.is() )
78 {
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 );
84 }
85
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;
90
91 // at last, try UNO range list
92 uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
93 if( xRanges.is() )
94 {
95 uno::Sequence< table::CellRangeAddress > aRangeAddresses = xRanges->getRangeAddresses();
96 if( aRangeAddresses.hasElements() )
97 return aRangeAddresses[ 0 ].Sheet;
98 }
99
100 throw lang::IllegalArgumentException();
101 }
102
103 /** Returns the AWT container window of the passed controller. */
lclGetWindowForController(const uno::Reference<frame::XController> & rxController)104 uno::Reference< awt::XWindow > lclGetWindowForController( const uno::Reference< frame::XController >& rxController )
105 {
106 if( rxController.is() ) try
107 {
108 uno::Reference< frame::XFrame > xFrame( rxController->getFrame(), uno::UNO_SET_THROW );
109 return xFrame->getContainerWindow();
110 }
111 catch( uno::Exception& )
112 {
113 }
114 return nullptr;
115 }
116
117 } // namespace
118
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 >
124 {
125 public:
126 ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell );
127
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 );
132
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;
141
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;
147
148 // XBorderResizeListener
149 virtual void SAL_CALL borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& aNewSize ) override;
150
151 // XChangesListener
152 virtual void SAL_CALL changesOccurred( const util::ChangesEvent& rEvent ) override;
153
154 // XEventListener
155 virtual void SAL_CALL disposing( const lang::EventObject& rEvent ) override;
156
157 private:
158 /** Starts listening to the document model. */
159 void startModelListening();
160 /** Stops listening to the document model. */
161 void stopModelListening();
162
163 /** Returns the controller for the passed VCL window. */
164 uno::Reference< frame::XController > getControllerForWindow( vcl::Window* pWindow ) const;
165
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 );
172
173 private:
174 typedef ::std::map< VclPtr<vcl::Window>, uno::Reference< frame::XController > > WindowControllerMap;
175
176 ::osl::Mutex maMutex;
177 ScVbaEventsHelper& mrVbaEvents;
178 uno::Reference< frame::XModel > mxModel;
179 ScDocShell* const 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;
186 };
187
ScVbaEventListener(ScVbaEventsHelper & rVbaEvents,const uno::Reference<frame::XModel> & rxModel,ScDocShell * pDocShell)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() )
196 {
197 if( !mxModel.is() )
198 return;
199
200 startModelListening();
201 try
202 {
203 uno::Reference< frame::XController > xController( mxModel->getCurrentController(), uno::UNO_SET_THROW );
204 startControllerListening( xController );
205 }
206 catch( uno::Exception& )
207 {
208 }
209 }
210
startControllerListening(const uno::Reference<frame::XController> & rxController)211 void ScVbaEventListener::startControllerListening( const uno::Reference< frame::XController >& rxController )
212 {
213 ::osl::MutexGuard aGuard( maMutex );
214
215 uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
216 if( xWindow.is() )
217 try { xWindow->addWindowListener( this ); } catch( uno::Exception& ) {}
218
219 uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
220 if( xTopWindow.is() )
221 try { xTopWindow->addTopWindowListener( this ); } catch( uno::Exception& ) {}
222
223 uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
224 if( xControllerBorder.is() )
225 try { xControllerBorder->addBorderResizeListener( this ); } catch( uno::Exception& ) {}
226
227 if( VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ) )
228 {
229 maControllers[ pWindow ] = rxController;
230 }
231 }
232
stopControllerListening(const uno::Reference<frame::XController> & rxController)233 void ScVbaEventListener::stopControllerListening( const uno::Reference< frame::XController >& rxController )
234 {
235 ::osl::MutexGuard aGuard( maMutex );
236
237 uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
238 if( xWindow.is() )
239 try { xWindow->removeWindowListener( this ); } catch( uno::Exception& ) {}
240
241 uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
242 if( xTopWindow.is() )
243 try { xTopWindow->removeTopWindowListener( this ); } catch( uno::Exception& ) {}
244
245 uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
246 if( xControllerBorder.is() )
247 try { xControllerBorder->removeBorderResizeListener( this ); } catch( uno::Exception& ) {}
248
249 if( VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ) )
250 {
251 maControllers.erase( pWindow );
252 if( pWindow == mpActiveWindow )
253 mpActiveWindow = nullptr;
254 }
255 }
256
windowOpened(const lang::EventObject &)257 void SAL_CALL ScVbaEventListener::windowOpened( const lang::EventObject& /*rEvent*/ )
258 {
259 }
260
windowClosing(const lang::EventObject &)261 void SAL_CALL ScVbaEventListener::windowClosing( const lang::EventObject& /*rEvent*/ )
262 {
263 }
264
windowClosed(const lang::EventObject &)265 void SAL_CALL ScVbaEventListener::windowClosed( const lang::EventObject& /*rEvent*/ )
266 {
267 }
268
windowMinimized(const lang::EventObject &)269 void SAL_CALL ScVbaEventListener::windowMinimized( const lang::EventObject& /*rEvent*/ )
270 {
271 }
272
windowNormalized(const lang::EventObject &)273 void SAL_CALL ScVbaEventListener::windowNormalized( const lang::EventObject& /*rEvent*/ )
274 {
275 }
276
windowActivated(const lang::EventObject & rEvent)277 void SAL_CALL ScVbaEventListener::windowActivated( const lang::EventObject& rEvent )
278 {
279 ::osl::MutexGuard aGuard( maMutex );
280
281 if( !mbDisposed )
282 {
283 uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
284 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
285 // do not fire activation event multiple time for the same window
286 if( pWindow && (pWindow != mpActiveWindow) )
287 {
288 // if another window is active, fire deactivation event first
289 if( mpActiveWindow )
290 processWindowActivateEvent( mpActiveWindow, false );
291 // fire activation event for the new window
292 processWindowActivateEvent( pWindow, true );
293 mpActiveWindow = pWindow;
294 }
295 }
296 }
297
windowDeactivated(const lang::EventObject & rEvent)298 void SAL_CALL ScVbaEventListener::windowDeactivated( const lang::EventObject& rEvent )
299 {
300 ::osl::MutexGuard aGuard( maMutex );
301
302 if( !mbDisposed )
303 {
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;
311 }
312 }
313
windowResized(const awt::WindowEvent & rEvent)314 void SAL_CALL ScVbaEventListener::windowResized( const awt::WindowEvent& rEvent )
315 {
316 ::osl::MutexGuard aGuard( maMutex );
317
318 mbWindowResized = true;
319 if( !mbDisposed && mbBorderChanged )
320 {
321 uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
322 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
323 }
324 }
325
windowMoved(const awt::WindowEvent &)326 void SAL_CALL ScVbaEventListener::windowMoved( const awt::WindowEvent& /*rEvent*/ )
327 {
328 }
329
windowShown(const lang::EventObject &)330 void SAL_CALL ScVbaEventListener::windowShown( const lang::EventObject& /*rEvent*/ )
331 {
332 }
333
windowHidden(const lang::EventObject &)334 void SAL_CALL ScVbaEventListener::windowHidden( const lang::EventObject& /*rEvent*/ )
335 {
336 }
337
borderWidthsChanged(const uno::Reference<uno::XInterface> & rSource,const frame::BorderWidths &)338 void SAL_CALL ScVbaEventListener::borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& /*aNewSize*/ )
339 {
340 ::osl::MutexGuard aGuard( maMutex );
341
342 mbBorderChanged = true;
343 if( !mbDisposed && mbWindowResized )
344 {
345 uno::Reference< frame::XController > xController( rSource, uno::UNO_QUERY );
346 uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( xController );
347 postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
348 }
349 }
350
changesOccurred(const util::ChangesEvent & rEvent)351 void SAL_CALL ScVbaEventListener::changesOccurred( const util::ChangesEvent& rEvent )
352 {
353 ::osl::MutexGuard aGuard( maMutex );
354
355 sal_Int32 nCount = rEvent.Changes.getLength();
356 if( mbDisposed || !mpDocShell || (nCount == 0) )
357 return;
358
359 util::ElementChange aChange = rEvent.Changes[ 0 ];
360 OUString sOperation;
361 aChange.Accessor >>= sOperation;
362 if( !sOperation.equalsIgnoreAsciiCase("cell-change") )
363 return;
364
365 if( nCount == 1 )
366 {
367 uno::Reference< table::XCellRange > xRangeObj;
368 aChange.ReplacedElement >>= xRangeObj;
369 if( xRangeObj.is() )
370 {
371 uno::Sequence< uno::Any > aArgs( 1 );
372 aArgs[0] <<= xRangeObj;
373 mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
374 }
375 return;
376 }
377
378 ScRangeList aRangeList;
379 for( const util::ElementChange& rChange : rEvent.Changes )
380 {
381 rChange.Accessor >>= sOperation;
382 uno::Reference< table::XCellRange > xRangeObj;
383 rChange.ReplacedElement >>= xRangeObj;
384 if( xRangeObj.is() && sOperation.equalsIgnoreAsciiCase("cell-change") )
385 {
386 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable( xRangeObj, uno::UNO_QUERY );
387 if( xCellRangeAddressable.is() )
388 {
389 ScRange aRange;
390 ScUnoConversion::FillScRange( aRange, xCellRangeAddressable->getRangeAddress() );
391 aRangeList.push_back( aRange );
392 }
393 }
394 }
395
396 if (!aRangeList.empty())
397 {
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 );
402 }
403 }
404
disposing(const lang::EventObject & rEvent)405 void SAL_CALL ScVbaEventListener::disposing( const lang::EventObject& rEvent )
406 {
407 ::osl::MutexGuard aGuard( maMutex );
408
409 uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
410 if( xModel.is() )
411 {
412 OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
413 stopModelListening();
414 mbDisposed = true;
415 return;
416 }
417
418 uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
419 if( xController.is() )
420 {
421 stopControllerListening( xController );
422 return;
423 }
424 }
425
426 // private --------------------------------------------------------------------
427
startModelListening()428 void ScVbaEventListener::startModelListening()
429 {
430 try
431 {
432 uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
433 xChangesNotifier->addChangesListener( this );
434 }
435 catch( uno::Exception& )
436 {
437 }
438 }
439
stopModelListening()440 void ScVbaEventListener::stopModelListening()
441 {
442 try
443 {
444 uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
445 xChangesNotifier->removeChangesListener( this );
446 }
447 catch( uno::Exception& )
448 {
449 }
450 }
451
getControllerForWindow(vcl::Window * pWindow) const452 uno::Reference< frame::XController > ScVbaEventListener::getControllerForWindow( vcl::Window* pWindow ) const
453 {
454 WindowControllerMap::const_iterator aIt = maControllers.find( pWindow );
455 return (aIt == maControllers.end()) ? uno::Reference< frame::XController >() : aIt->second;
456 }
457
processWindowActivateEvent(vcl::Window * pWindow,bool bActivate)458 void ScVbaEventListener::processWindowActivateEvent( vcl::Window* pWindow, bool bActivate )
459 {
460 uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
461 if( xController.is() )
462 {
463 uno::Sequence< uno::Any > aArgs( 1 );
464 aArgs[ 0 ] <<= xController;
465 mrVbaEvents.processVbaEventNoThrow( bActivate ? WORKBOOK_WINDOWACTIVATE : WORKBOOK_WINDOWDEACTIVATE, aArgs );
466 }
467 }
468
postWindowResizeEvent(vcl::Window * pWindow)469 void ScVbaEventListener::postWindowResizeEvent( vcl::Window* pWindow )
470 {
471 // check that the passed window is still alive (it must be registered in maControllers)
472 if( pWindow && (maControllers.count( pWindow ) > 0) )
473 {
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 );
478 }
479 }
480
IMPL_LINK(ScVbaEventListener,processWindowResizeEvent,void *,p,void)481 IMPL_LINK( ScVbaEventListener, processWindowResizeEvent, void*, p, void )
482 {
483 vcl::Window* pWindow = static_cast<vcl::Window*>(p);
484 ::osl::MutexGuard aGuard( maMutex );
485
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) )
495 {
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 )
499 {
500 uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
501 if( xController.is() )
502 {
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 );
507 }
508 }
509 }
510 {
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);
517 }
518 release();
519 }
520
ScVbaEventsHelper(const uno::Sequence<uno::Any> & rArgs)521 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs ) :
522 VbaEventsHelperBase( rArgs ),
523 mbOpened( false )
524 {
525 mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
526 mpDoc = mpDocShell ? &mpDocShell->GetDocument() : nullptr;
527
528 if( !mxModel.is() || !mpDocShell || !mpDoc )
529 return;
530
531 // global
532 auto registerAutoEvent = [this](sal_Int32 nID, const sal_Char* sName)
533 { registerEventHandler(nID, script::ModuleType::NORMAL, (OString("Auto_").concat(sName)).getStr(), -1, uno::Any(false)); };
534 registerAutoEvent(AUTO_OPEN, "Open");
535 registerAutoEvent(AUTO_CLOSE, "Close");
536
537 // Workbook
538 auto registerWorkbookEvent = [this](sal_Int32 nID, const sal_Char* sName, sal_Int32 nCancelIndex)
539 { registerEventHandler(nID, script::ModuleType::DOCUMENT, (OString("Workbook_").concat(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 );
551
552 // Worksheet events. All events have a corresponding workbook event.
553 auto registerWorksheetEvent = [this](sal_Int32 nID, const sal_Char* sName, sal_Int32 nCancelIndex)
554 {
555 registerEventHandler(nID, script::ModuleType::DOCUMENT, (OString("Worksheet_").concat(sName)).getStr(),
556 nCancelIndex, uno::Any(true));
557 registerEventHandler(USERDEFINED_START + nID, script::ModuleType::DOCUMENT,
558 (OString("Workbook_Worksheet").concat(sName)).getStr(),
559 ((nCancelIndex >= 0) ? (nCancelIndex + 1) : -1), uno::Any(false));
560 };
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 );
569 }
570
~ScVbaEventsHelper()571 ScVbaEventsHelper::~ScVbaEventsHelper()
572 {
573 }
574
notifyEvent(const css::document::EventObject & rEvent)575 void SAL_CALL ScVbaEventsHelper::notifyEvent( const css::document::EventObject& rEvent )
576 {
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
580 {
581 processVbaEventNoThrow( WORKBOOK_OPEN, saEmptyArgs );
582 }
583 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC ) )
584 {
585 processVbaEventNoThrow( WORKBOOK_ACTIVATE, saEmptyArgs );
586 }
587 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC ) )
588 {
589 processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
590 }
591 else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCDONE )) ||
592 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCDONE )) ||
593 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCDONE )) )
594 {
595 uno::Sequence< uno::Any > aArgs( 1 );
596 aArgs[ 0 ] <<= true;
597 processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
598 }
599 else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCFAILED )) ||
600 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCFAILED )) ||
601 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCFAILED )) )
602 {
603 uno::Sequence< uno::Any > aArgs( 1 );
604 aArgs[ 0 ] <<= false;
605 processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
606 }
607 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ) )
608 {
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() )
613 {
614 uno::Sequence< uno::Any > aArgs( 1 );
615 aArgs[ 0 ] <<= xController;
616 processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE, aArgs );
617 }
618 processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
619 }
620 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED ) )
621 {
622 uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
623 if( mxListener.get() && xController.is() )
624 mxListener->startControllerListening( xController );
625 }
626 VbaEventsHelperBase::notifyEvent( rEvent );
627 }
628
getImplementationName()629 OUString ScVbaEventsHelper::getImplementationName()
630 {
631 return "ScVbaEventsHelper";
632 }
633
getSupportedServiceNames()634 css::uno::Sequence<OUString> ScVbaEventsHelper::getSupportedServiceNames()
635 {
636 return css::uno::Sequence<OUString>{
637 "com.sun.star.script.vba.VBASpreadsheetEventProcessor"};
638 }
639
640 // protected ------------------------------------------------------------------
641
implPrepareEvent(EventQueue & rEventQueue,const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs)642 bool ScVbaEventsHelper::implPrepareEvent( EventQueue& rEventQueue,
643 const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& rArgs )
644 {
645 // document and document shell are needed during event processing
646 if( !mpShell || !mpDoc )
647 throw uno::RuntimeException();
648
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();
655
656 // framework and Calc fire a few events before 'OnLoad', ignore them
657 if( bExecuteEvent )
658 bExecuteEvent = (rInfo.mnEventId == WORKBOOK_OPEN) ? !mbOpened : mbOpened;
659
660 // special handling for some events
661 if( bExecuteEvent ) switch( rInfo.mnEventId )
662 {
663 case WORKBOOK_OPEN:
664 {
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();
673 }
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;
679 }
680
681 if( bExecuteEvent )
682 {
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 );
687 }
688
689 return bExecuteEvent;
690 }
691
implBuildArgumentList(const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs)692 uno::Sequence< uno::Any > ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& rInfo,
693 const uno::Sequence< uno::Any >& rArgs )
694 {
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;
698
699 uno::Sequence< uno::Any > aVbaArgs;
700 switch( nEventId )
701 {
702 // *** Workbook ***
703
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;
740
741 // *** Worksheet ***
742
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;
766 }
767
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 )
771 {
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;
777 }
778
779 return aVbaArgs;
780 }
781
implPostProcessEvent(EventQueue & rEventQueue,const EventHandlerInfo & rInfo,bool bCancel)782 void ScVbaEventsHelper::implPostProcessEvent( EventQueue& rEventQueue,
783 const EventHandlerInfo& rInfo, bool bCancel )
784 {
785 switch( rInfo.mnEventId )
786 {
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;
799 }
800 }
801
implGetDocumentModuleName(const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs) const802 OUString ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& rInfo,
803 const uno::Sequence< uno::Any >& rArgs ) const
804 {
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();
810
811 OUString aCodeName;
812 if( bSheetEvent )
813 mpDoc->GetCodeName( nTab, aCodeName );
814 else
815 aCodeName = mpDoc->GetCodeName();
816 return aCodeName;
817 }
818
819 // private --------------------------------------------------------------------
820
821 namespace {
822
823 /** Compares the passed range lists representing sheet selections. Ignores
824 selections that refer to different sheets (returns false in this case). */
lclSelectionChanged(const ScRangeList & rLeft,const ScRangeList & rRight)825 bool lclSelectionChanged( const ScRangeList& rLeft, const ScRangeList& rRight )
826 {
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);
832
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;
836
837 // compare all ranges
838 return rLeft != rRight;
839 }
840
841 } // namespace
842
isSelectionChanged(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex)843 bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex )
844 {
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;
852 }
853
createWorksheet(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const854 uno::Any ScVbaEventsHelper::createWorksheet( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
855 {
856 // extract sheet index, will throw, if parameter is invalid
857 SCTAB nTab = lclGetTabFromArgs( rArgs, nIndex );
858 return uno::Any( excel::getUnoSheetModuleObj( mxModel, nTab ) );
859 }
860
createRange(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const861 uno::Any ScVbaEventsHelper::createRange( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
862 {
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() )
866 {
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();
871
872 uno::Sequence< uno::Any > aArgs( 2 );
873 if ( xRanges.is() )
874 {
875 aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRanges );
876 aArgs[ 1 ] <<= xRanges;
877 }
878 else
879 {
880 aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRange );
881 aArgs[ 1 ] <<= xRange;
882 }
883 xVbaRange.set( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Range", aArgs ), uno::UNO_QUERY_THROW );
884 }
885 return uno::Any( xVbaRange );
886 }
887
createHyperlink(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const888 uno::Any ScVbaEventsHelper::createHyperlink( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
889 {
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 );
896 }
897
createWindow(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const898 uno::Any ScVbaEventsHelper::createWindow( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
899 {
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 );
906 }
907
908 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
ScVbaEventsHelper_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const & arguments)909 ScVbaEventsHelper_get_implementation(
910 css::uno::XComponentContext * /*context*/,
911 css::uno::Sequence<css::uno::Any> const &arguments)
912 {
913 return cppu::acquire(new ScVbaEventsHelper(arguments));
914 }
915
916 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
917