LibreOffice Module sc (master) 1
vbaeventshelper.cxx
Go to the documentation of this file.
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
38#include <unotools/eventcfg.hxx>
39#include <vcl/event.hxx>
40#include <vcl/svapp.hxx>
41#include <vcl/window.hxx>
43
44#include <docsh.hxx>
45#include <document.hxx>
46#include <cellsuno.hxx>
47#include <convuno.hxx>
48#include "vbaapplication.hxx"
49
50using namespace ::com::sun::star;
51using namespace ::com::sun::star::script::vba::VBAEventId;
52using namespace ::ooo::vba;
53
54namespace {
55
62SCTAB lclGetTabFromArgs( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex )
63{
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
104uno::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
120class ScVbaEventListener : public ::cppu::WeakImplHelper< awt::XTopWindowListener,
121 awt::XWindowListener,
122 frame::XBorderResizeListener,
123 util::XChangesListener >
124{
125public:
126 ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell );
127
129 void startControllerListening( const uno::Reference< frame::XController >& rxController );
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
157private:
159 void startModelListening();
161 void stopModelListening();
162
164 uno::Reference< frame::XController > getControllerForWindow( vcl::Window* pWindow ) const;
165
167 void processWindowActivateEvent( vcl::Window* pWindow, bool bActivate );
169 void postWindowResizeEvent( vcl::Window* pWindow );
171 DECL_LINK( processWindowResizeEvent, void*, void );
172
173private:
174 typedef ::std::map< VclPtr<vcl::Window>, uno::Reference< frame::XController > > WindowControllerMap;
175
176 ::osl::Mutex maMutex;
178 uno::Reference< frame::XModel > mxModel;
181 std::multiset< VclPtr<vcl::Window> > m_PostedWindows;
186};
187
188ScVbaEventListener::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
201 try
202 {
203 uno::Reference< frame::XController > xController( mxModel->getCurrentController(), uno::UNO_SET_THROW );
205 }
206 catch( uno::Exception& )
207 {
208 }
209}
210
211void 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
233void 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
257void SAL_CALL ScVbaEventListener::windowOpened( const lang::EventObject& /*rEvent*/ )
258{
259}
260
261void SAL_CALL ScVbaEventListener::windowClosing( const lang::EventObject& /*rEvent*/ )
262{
263}
264
265void SAL_CALL ScVbaEventListener::windowClosed( const lang::EventObject& /*rEvent*/ )
266{
267}
268
269void SAL_CALL ScVbaEventListener::windowMinimized( const lang::EventObject& /*rEvent*/ )
270{
271}
272
273void SAL_CALL ScVbaEventListener::windowNormalized( const lang::EventObject& /*rEvent*/ )
274{
275}
276
277void SAL_CALL ScVbaEventListener::windowActivated( const lang::EventObject& rEvent )
278{
279 ::osl::MutexGuard aGuard( maMutex );
280
281 if( mbDisposed )
282 return;
283
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) )
288 {
289 // if another window is active, fire deactivation event first
290 if( mpActiveWindow )
292 // fire activation event for the new window
293 processWindowActivateEvent( pWindow, true );
294 mpActiveWindow = pWindow;
295 }
296}
297
298void 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
314void SAL_CALL ScVbaEventListener::windowResized( const awt::WindowEvent& rEvent )
315{
316 ::osl::MutexGuard aGuard( maMutex );
317
318 mbWindowResized = true;
320 {
321 uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
323 }
324}
325
326void SAL_CALL ScVbaEventListener::windowMoved( const awt::WindowEvent& /*rEvent*/ )
327{
328}
329
330void SAL_CALL ScVbaEventListener::windowShown( const lang::EventObject& /*rEvent*/ )
331{
332}
333
334void SAL_CALL ScVbaEventListener::windowHidden( const lang::EventObject& /*rEvent*/ )
335{
336}
337
338void SAL_CALL ScVbaEventListener::borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& /*aNewSize*/ )
339{
340 ::osl::MutexGuard aGuard( maMutex );
341
342 mbBorderChanged = true;
344 {
345 uno::Reference< frame::XController > xController( rSource, uno::UNO_QUERY );
346 uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( xController );
348 }
349}
350
351void 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{ uno::Any(xRangeObj) };
372 mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
373 }
374 return;
375 }
376
377 ScRangeList aRangeList;
378 for( const util::ElementChange& rChange : rEvent.Changes )
379 {
380 rChange.Accessor >>= sOperation;
381 uno::Reference< table::XCellRange > xRangeObj;
382 rChange.ReplacedElement >>= xRangeObj;
383 if( xRangeObj.is() && sOperation.equalsIgnoreAsciiCase("cell-change") )
384 {
385 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable( xRangeObj, uno::UNO_QUERY );
386 if( xCellRangeAddressable.is() )
387 {
388 ScRange aRange;
389 ScUnoConversion::FillScRange( aRange, xCellRangeAddressable->getRangeAddress() );
390 aRangeList.push_back( aRange );
391 }
392 }
393 }
394
395 if (!aRangeList.empty())
396 {
397 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( mpDocShell, aRangeList ) );
398 uno::Sequence< uno::Any > aArgs{ uno::Any(xRanges) };
399 mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
400 }
401}
402
403void SAL_CALL ScVbaEventListener::disposing( const lang::EventObject& rEvent )
404{
405 ::osl::MutexGuard aGuard( maMutex );
406
407 uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
408 if( xModel.is() )
409 {
410 OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
412 mbDisposed = true;
413 return;
414 }
415
416 uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
417 if( xController.is() )
418 {
420 return;
421 }
422}
423
424// private --------------------------------------------------------------------
425
427{
428 try
429 {
430 uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
431 xChangesNotifier->addChangesListener( this );
432 }
433 catch( uno::Exception& )
434 {
435 }
436}
437
439{
440 try
441 {
442 uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
443 xChangesNotifier->removeChangesListener( this );
444 }
445 catch( uno::Exception& )
446 {
447 }
448}
449
450uno::Reference< frame::XController > ScVbaEventListener::getControllerForWindow( vcl::Window* pWindow ) const
451{
452 WindowControllerMap::const_iterator aIt = maControllers.find( pWindow );
453 return (aIt == maControllers.end()) ? uno::Reference< frame::XController >() : aIt->second;
454}
455
457{
458 uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
459 if( xController.is() )
460 {
461 uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
462 mrVbaEvents.processVbaEventNoThrow( bActivate ? WORKBOOK_WINDOWACTIVATE : WORKBOOK_WINDOWDEACTIVATE, aArgs );
463 }
464}
465
467{
468 // check that the passed window is still alive (it must be registered in maControllers)
469 if( pWindow && (maControllers.count( pWindow ) > 0) )
470 {
472 acquire(); // ensure we don't get deleted before the timer fires
473 m_PostedWindows.insert(pWindow);
474 Application::PostUserEvent( LINK( this, ScVbaEventListener, processWindowResizeEvent ), pWindow );
475 }
476}
477
478IMPL_LINK( ScVbaEventListener, processWindowResizeEvent, void*, p, void )
479{
480 vcl::Window* pWindow = static_cast<vcl::Window*>(p);
481 ::osl::MutexGuard aGuard( maMutex );
482
483 /* Check that the passed window is still alive (it must be registered in
484 maControllers). While closing a document, postWindowResizeEvent() may
485 be called on the last window which posts a user event via
486 Application::PostUserEvent to call this event handler. VCL will trigger
487 the handler some time later. Sometimes, the window gets deleted before.
488 This is handled via the disposing() function which removes the window
489 pointer from the member maControllers. Thus, checking whether
490 maControllers contains pWindow ensures that the window is still alive. */
491 if( !mbDisposed && pWindow && !pWindow->isDisposed() && (maControllers.count(pWindow) > 0) )
492 {
493 // do not fire event unless all mouse buttons have been released
494 vcl::Window::PointerState aPointerState = pWindow->GetPointerState();
495 if( (aPointerState.mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0 )
496 {
497 uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
498 if( xController.is() )
499 {
500 uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
501 // #163419# do not throw exceptions into application core
502 mrVbaEvents.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE, aArgs );
503 }
504 }
505 }
506 {
507 // note: there may be multiple processWindowResizeEvent outstanding
508 // for pWindow, so it may have been added to m_PostedWindows multiple
509 // times - so this must delete exactly one of these elements!
510 auto const iter(m_PostedWindows.find(pWindow));
511 assert(iter != m_PostedWindows.end());
512 m_PostedWindows.erase(iter);
513 }
514 release();
515}
516
517ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs ) :
518 VbaEventsHelperBase( rArgs ),
519 mbOpened( false )
520{
521 mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
522 mpDoc = mpDocShell ? &mpDocShell->GetDocument() : nullptr;
523
524 if( !mxModel.is() || !mpDocShell || !mpDoc )
525 return;
526
527 // global
528 auto registerAutoEvent = [this](sal_Int32 nID, const char* sName)
529 { registerEventHandler(nID, script::ModuleType::NORMAL, OString(OString::Concat("Auto_") + sName).getStr(), -1, uno::Any(false)); };
530 registerAutoEvent(AUTO_OPEN, "Open");
531 registerAutoEvent(AUTO_CLOSE, "Close");
532
533 // Workbook
534 auto registerWorkbookEvent = [this](sal_Int32 nID, const char* sName, sal_Int32 nCancelIndex)
535 { registerEventHandler(nID, script::ModuleType::DOCUMENT, OString(OString::Concat("Workbook_") + sName).getStr(), nCancelIndex, uno::Any(false)); };
536 registerWorkbookEvent( WORKBOOK_ACTIVATE, "Activate", -1 );
537 registerWorkbookEvent( WORKBOOK_DEACTIVATE, "Deactivate", -1 );
538 registerWorkbookEvent( WORKBOOK_OPEN, "Open", -1 );
539 registerWorkbookEvent( WORKBOOK_BEFORECLOSE, "BeforeClose", 0 );
540 registerWorkbookEvent( WORKBOOK_BEFOREPRINT, "BeforePrint", 0 );
541 registerWorkbookEvent( WORKBOOK_BEFORESAVE, "BeforeSave", 1 );
542 registerWorkbookEvent( WORKBOOK_AFTERSAVE, "AfterSave", -1 );
543 registerWorkbookEvent( WORKBOOK_NEWSHEET, "NewSheet", -1 );
544 registerWorkbookEvent( WORKBOOK_WINDOWACTIVATE, "WindowActivate", -1 );
545 registerWorkbookEvent( WORKBOOK_WINDOWDEACTIVATE, "WindowDeactivate", -1 );
546 registerWorkbookEvent( WORKBOOK_WINDOWRESIZE, "WindowResize", -1 );
547
548 // Worksheet events. All events have a corresponding workbook event.
549 auto registerWorksheetEvent = [this](sal_Int32 nID, const char* sName, sal_Int32 nCancelIndex)
550 {
551 registerEventHandler(nID, script::ModuleType::DOCUMENT, OString(OString::Concat("Worksheet_") + sName).getStr(),
552 nCancelIndex, uno::Any(true));
553 registerEventHandler(USERDEFINED_START + nID, script::ModuleType::DOCUMENT,
554 OString(OString::Concat("Workbook_Worksheet") + sName).getStr(),
555 ((nCancelIndex >= 0) ? (nCancelIndex + 1) : -1), uno::Any(false));
556 };
557 registerWorksheetEvent( WORKSHEET_ACTIVATE, "Activate", -1 );
558 registerWorksheetEvent( WORKSHEET_DEACTIVATE, "Deactivate", -1 );
559 registerWorksheetEvent( WORKSHEET_BEFOREDOUBLECLICK, "BeforeDoubleClick", 1 );
560 registerWorksheetEvent( WORKSHEET_BEFORERIGHTCLICK, "BeforeRightClick", 1 );
561 registerWorksheetEvent( WORKSHEET_CALCULATE, "Calculate", -1 );
562 registerWorksheetEvent( WORKSHEET_CHANGE, "Change", -1 );
563 registerWorksheetEvent( WORKSHEET_SELECTIONCHANGE, "SelectionChange", -1 );
564 registerWorksheetEvent( WORKSHEET_FOLLOWHYPERLINK, "FollowHyperlink", -1 );
565}
566
568{
569}
570
571void SAL_CALL ScVbaEventsHelper::notifyEvent( const css::document::EventObject& rEvent )
572{
573 static const uno::Sequence< uno::Any > saEmptyArgs;
574 if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC )) ||
575 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CREATEDOC )) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
576 {
577 processVbaEventNoThrow( WORKBOOK_OPEN, saEmptyArgs );
578 }
579 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC ) )
580 {
581 processVbaEventNoThrow( WORKBOOK_ACTIVATE, saEmptyArgs );
582 }
583 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC ) )
584 {
585 processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
586 }
587 else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCDONE )) ||
588 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCDONE )) ||
589 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCDONE )) )
590 {
591 uno::Sequence< uno::Any > aArgs{ uno::Any(true) };
592 processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
593 }
594 else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCFAILED )) ||
595 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCFAILED )) ||
596 (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCFAILED )) )
597 {
598 uno::Sequence< uno::Any > aArgs{ uno::Any(false) };
599 processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
600 }
601 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ) )
602 {
603 /* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
604 events and stop listening to the model (done in base class). */
605 uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
606 if( xController.is() )
607 {
608 uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
609 processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE, aArgs );
610 }
611 processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
612 }
613 else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED ) )
614 {
615 uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
616 if( mxListener && xController.is() )
617 mxListener->startControllerListening( xController );
618 }
620}
621
623{
624 return "ScVbaEventsHelper";
625}
626
628{
629 return css::uno::Sequence<OUString>{
630 "com.sun.star.script.vba.VBASpreadsheetEventProcessor"};
631}
632
633// protected ------------------------------------------------------------------
634
636 const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& rArgs )
637{
638 // document and document shell are needed during event processing
639 if( !mpShell || !mpDoc )
640 throw uno::RuntimeException();
641
642 /* For document events: check if events are enabled via the
643 Application.EnableEvents symbol (this is an Excel-only attribute).
644 Check this again for every event, as the event handler may change the
645 state of the EnableEvents symbol. Global events such as AUTO_OPEN and
646 AUTO_CLOSE are always enabled. */
647 bool bExecuteEvent = (rInfo.mnModuleType != script::ModuleType::DOCUMENT) || ScVbaApplication::getDocumentEventsEnabled();
648
649 // framework and Calc fire a few events before 'OnLoad', ignore them
650 if( bExecuteEvent )
651 bExecuteEvent = (rInfo.mnEventId == WORKBOOK_OPEN) ? !mbOpened : mbOpened;
652
653 // special handling for some events
654 if( bExecuteEvent ) switch( rInfo.mnEventId )
655 {
656 case WORKBOOK_OPEN:
657 {
658 // execute delayed Activate event too (see above)
659 rEventQueue.emplace_back(WORKBOOK_ACTIVATE );
660 uno::Sequence< uno::Any > aArgs{ uno::Any(mxModel->getCurrentController()) };
661 rEventQueue.emplace_back( WORKBOOK_WINDOWACTIVATE, aArgs );
662 rEventQueue.emplace_back(AUTO_OPEN );
663 // remember initial selection
664 maOldSelection <<= mxModel->getCurrentSelection();
665 }
666 break;
667 case WORKSHEET_SELECTIONCHANGE:
668 // if selection is not changed, then do not fire the event
669 bExecuteEvent = isSelectionChanged( rArgs, 0 );
670 break;
671 }
672
673 if( bExecuteEvent )
674 {
675 // add workbook event associated to a sheet event
676 bool bSheetEvent = false;
677 if( (rInfo.maUserData >>= bSheetEvent) && bSheetEvent )
678 rEventQueue.emplace_back( rInfo.mnEventId + USERDEFINED_START, rArgs );
679 }
680
681 return bExecuteEvent;
682}
683
684uno::Sequence< uno::Any > ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& rInfo,
685 const uno::Sequence< uno::Any >& rArgs )
686{
687 // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
688 bool bSheetEventAsBookEvent = rInfo.mnEventId > USERDEFINED_START;
689 sal_Int32 nEventId = bSheetEventAsBookEvent ? (rInfo.mnEventId - USERDEFINED_START) : rInfo.mnEventId;
690
691 uno::Sequence< uno::Any > aVbaArgs;
692 switch( nEventId )
693 {
694 // *** Workbook ***
695
696 // no arguments
697 case WORKBOOK_ACTIVATE:
698 case WORKBOOK_DEACTIVATE:
699 case WORKBOOK_OPEN:
700 break;
701 // 1 arg: cancel
702 case WORKBOOK_BEFORECLOSE:
703 case WORKBOOK_BEFOREPRINT:
704 aVbaArgs.realloc( 1 );
705 // current cancel state will be inserted by caller
706 break;
707 // 2 args: saveAs, cancel
708 case WORKBOOK_BEFORESAVE:
709 checkArgumentType< bool >( rArgs, 0 );
710 aVbaArgs = { rArgs[ 0 ], {} };
711 // current cancel state will be inserted by caller
712 break;
713 // 1 arg: success
714 case WORKBOOK_AFTERSAVE:
715 checkArgumentType< bool >( rArgs, 0 );
716 aVbaArgs = { rArgs[ 0 ] };
717 break;
718 // 1 arg: window
719 case WORKBOOK_WINDOWACTIVATE:
720 case WORKBOOK_WINDOWDEACTIVATE:
721 case WORKBOOK_WINDOWRESIZE:
722 aVbaArgs = { createWindow( rArgs, 0 ) };
723 break;
724 // 1 arg: worksheet
725 case WORKBOOK_NEWSHEET:
726 aVbaArgs = { createWorksheet( rArgs, 0 ) };
727 break;
728
729 // *** Worksheet ***
730
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 = { createRange( rArgs, 0 ) };
740 break;
741 // 2 args: range, cancel
742 case WORKSHEET_BEFOREDOUBLECLICK:
743 case WORKSHEET_BEFORERIGHTCLICK:
744 aVbaArgs = { createRange( rArgs, 0 ), {} };
745 // current cancel state will be inserted by caller
746 break;
747 // 1 arg: hyperlink
748 case WORKSHEET_FOLLOWHYPERLINK:
749 aVbaArgs = { createHyperlink( rArgs, 0 ) };
750 break;
751 }
752
753 /* For workbook events associated to sheet events, the workbook event gets
754 the same arguments but with a Worksheet object in front of them. */
755 if( bSheetEventAsBookEvent )
756 {
757 sal_Int32 nLength = aVbaArgs.getLength();
758 uno::Sequence< uno::Any > aVbaArgs2( nLength + 1 );
759 auto pVbaArgs2 = aVbaArgs2.getArray();
760 *pVbaArgs2 = createWorksheet( rArgs, 0 );
761 std::copy_n(std::cbegin(aVbaArgs), nLength, std::next(pVbaArgs2));
762 aVbaArgs = aVbaArgs2;
763 }
764
765 return aVbaArgs;
766}
767
769 const EventHandlerInfo& rInfo, bool bCancel )
770{
771 switch( rInfo.mnEventId )
772 {
773 case WORKBOOK_OPEN:
774 mbOpened = true;
775 // register the listeners
776 if( !mxListener.is() )
778 break;
779 case WORKBOOK_BEFORECLOSE:
780 /* Execute Auto_Close only if not cancelled by event handler, but
781 before UI asks user whether to cancel closing the document. */
782 if( !bCancel )
783 rEventQueue.emplace_back(AUTO_CLOSE );
784 break;
785 }
786}
787
788OUString ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& rInfo,
789 const uno::Sequence< uno::Any >& rArgs ) const
790{
791 bool bSheetEvent = false;
792 rInfo.maUserData >>= bSheetEvent;
793 SCTAB nTab = bSheetEvent ? lclGetTabFromArgs( rArgs, 0 ) : -1;
794 if( bSheetEvent && (nTab < 0) )
795 throw lang::IllegalArgumentException();
796
797 OUString aCodeName;
798 if( bSheetEvent )
799 mpDoc->GetCodeName( nTab, aCodeName );
800 else
801 aCodeName = mpDoc->GetCodeName();
802 return aCodeName;
803}
804
805// private --------------------------------------------------------------------
806
807namespace {
808
811bool lclSelectionChanged( const ScRangeList& rLeft, const ScRangeList& rRight )
812{
813 // one of the range lists empty? -> return false, if both lists empty
814 bool bLeftEmpty = rLeft.empty();
815 bool bRightEmpty = rRight.empty();
816 if( bLeftEmpty || bRightEmpty )
817 return !(bLeftEmpty && bRightEmpty);
818
819 // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
820 if (rLeft[0].aStart.Tab() != rRight[0].aStart.Tab())
821 return false;
822
823 // compare all ranges
824 return rLeft != rRight;
825}
826
827} // namespace
828
829bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex )
830{
831 uno::Reference< uno::XInterface > xOldSelection( maOldSelection, uno::UNO_QUERY );
832 uno::Reference< uno::XInterface > xNewSelection = getXSomethingFromArgs< uno::XInterface >( rArgs, nIndex, false );
833 ScCellRangesBase* pOldCellRanges = comphelper::getFromUnoTunnel<ScCellRangesBase>( xOldSelection );
834 ScCellRangesBase* pNewCellRanges = comphelper::getFromUnoTunnel<ScCellRangesBase>( xNewSelection );
835 bool bChanged = !pOldCellRanges || !pNewCellRanges || lclSelectionChanged( pOldCellRanges->GetRangeList(), pNewCellRanges->GetRangeList() );
836 maOldSelection <<= xNewSelection;
837 return bChanged;
838}
839
840uno::Any ScVbaEventsHelper::createWorksheet( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
841{
842 // extract sheet index, will throw, if parameter is invalid
843 SCTAB nTab = lclGetTabFromArgs( rArgs, nIndex );
845}
846
847uno::Any ScVbaEventsHelper::createRange( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
848{
849 // it is possible to pass an existing VBA Range object
850 uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
851 if( !xVbaRange.is() )
852 {
853 uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
854 uno::Reference< table::XCellRange > xRange = getXSomethingFromArgs< table::XCellRange >( rArgs, nIndex );
855 if ( !xRanges.is() && !xRange.is() )
856 throw lang::IllegalArgumentException();
857
858 uno::Sequence< uno::Any > aArgs;
859 if ( xRanges.is() )
860 {
861 aArgs = { uno::Any(excel::getUnoSheetModuleObj( xRanges )), uno::Any(xRanges) };
862 }
863 else
864 {
865 aArgs = { uno::Any(excel::getUnoSheetModuleObj( xRange )), uno::Any(xRange) };
866 }
867 xVbaRange.set( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Range", aArgs ), uno::UNO_QUERY_THROW );
868 }
869 return uno::Any( xVbaRange );
870}
871
872uno::Any ScVbaEventsHelper::createHyperlink( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
873{
874 uno::Reference< table::XCell > xCell = getXSomethingFromArgs< table::XCell >( rArgs, nIndex, false );
875 uno::Sequence< uno::Any > aArgs{ uno::Any(excel::getUnoSheetModuleObj( xCell )),
876 uno::Any(xCell) };
877 uno::Reference< uno::XInterface > xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Hyperlink", aArgs ), uno::UNO_SET_THROW );
878 return uno::Any( xHyperlink );
879}
880
881uno::Any ScVbaEventsHelper::createWindow( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
882{
883 uno::Sequence< uno::Any > aArgs{ uno::Any(getVBADocument( mxModel )),
885 uno::Any(getXSomethingFromArgs< frame::XController >( rArgs, nIndex, false )) };
886 uno::Reference< uno::XInterface > xWindow( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Window", aArgs ), uno::UNO_SET_THROW );
887 return uno::Any( xWindow );
888}
889
890extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
892 css::uno::XComponentContext * /*context*/,
893 css::uno::Sequence<css::uno::Any> const &arguments)
894{
895 return cppu::acquire(new ScVbaEventsHelper(arguments));
896}
897
898/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::frame::XModel2 > mxModel
std::mutex maMutex
const SCTAB MAXTAB
Definition: address.hxx:70
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
static OUString GetEventName(GlobalEventId nID)
const ScRangeList & GetRangeList() const
Definition: cellsuno.hxx:243
const ScDocument & GetDocument() const
Definition: docsh.hxx:220
const OUString & GetCodeName() const
Definition: document.hxx:608
bool empty() const
Definition: rangelst.hxx:88
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1137
static void FillScRange(ScRange &rScRange, const css::table::CellRangeAddress &rApiRange)
Definition: convuno.hxx:79
static bool getDocumentEventsEnabled()
Returns true, if VBA document events are enabled.
bool mbWindowResized
Currently activated window, to prevent multiple (de)activation.
uno::Reference< frame::XController > getControllerForWindow(vcl::Window *pWindow) const
Returns the controller for the passed VCL window.
ScVbaEventListener(ScVbaEventsHelper &rVbaEvents, const uno::Reference< frame::XModel > &rxModel, ScDocShell *pDocShell)
virtual void SAL_CALL windowActivated(const lang::EventObject &rEvent) override
std::multiset< VclPtr< vcl::Window > > m_PostedWindows
Maps VCL top windows to their controllers.
virtual void SAL_CALL windowMoved(const awt::WindowEvent &rEvent) override
void postWindowResizeEvent(vcl::Window *pWindow)
Posts a Workbook_WindowResize user event.
WindowControllerMap maControllers
virtual void SAL_CALL changesOccurred(const util::ChangesEvent &rEvent) override
virtual void SAL_CALL windowOpened(const lang::EventObject &rEvent) override
::std::map< VclPtr< vcl::Window >, uno::Reference< frame::XController > > WindowControllerMap
void startControllerListening(const uno::Reference< frame::XController > &rxController)
Starts listening to the passed document controller.
virtual void SAL_CALL borderWidthsChanged(const uno::Reference< uno::XInterface > &rSource, const frame::BorderWidths &aNewSize) override
bool mbBorderChanged
True = window resize system event processed.
DECL_LINK(processWindowResizeEvent, void *, void)
Callback link for Application::PostUserEvent().
virtual void SAL_CALL windowHidden(const lang::EventObject &rEvent) override
ScVbaEventsHelper & mrVbaEvents
void processWindowActivateEvent(vcl::Window *pWindow, bool bActivate)
Calls the Workbook_Window[Activate|Deactivate] event handler.
virtual void SAL_CALL windowDeactivated(const lang::EventObject &rEvent) override
void startModelListening()
Starts listening to the document model.
virtual void SAL_CALL windowShown(const lang::EventObject &rEvent) override
virtual void SAL_CALL windowNormalized(const lang::EventObject &rEvent) override
uno::Reference< frame::XModel > mxModel
VclPtr< vcl::Window > mpActiveWindow
Keeps processWindowResizeEvent windows from being deleted between postWindowResizeEvent and processWi...
bool mbDisposed
True = borders changed system event processed.
virtual void SAL_CALL windowMinimized(const lang::EventObject &rEvent) override
virtual void SAL_CALL windowClosed(const lang::EventObject &rEvent) override
void stopControllerListening(const uno::Reference< frame::XController > &rxController)
Stops listening to the passed document controller.
void stopModelListening()
Stops listening to the document model.
virtual void SAL_CALL disposing(const lang::EventObject &rEvent) override
virtual void SAL_CALL windowResized(const awt::WindowEvent &rEvent) override
virtual void SAL_CALL windowClosing(const lang::EventObject &rEvent) override
ScDocShell * mpDocShell
OUString SAL_CALL getImplementationName() override
::rtl::Reference< ScVbaEventListener > mxListener
virtual void SAL_CALL notifyEvent(const css::document::EventObject &rEvent) override
css::uno::Any createWorksheet(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Worksheet object (the argument must contain a sheet index).
css::uno::Any createHyperlink(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Hyperlink object (the argument must contain a UNO cell).
virtual void implPostProcessEvent(EventQueue &rEventQueue, const EventHandlerInfo &rInfo, bool bCancel) override
css::uno::Any createWindow(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Window object (the argument must contain a model controller).
virtual ~ScVbaEventsHelper() override
virtual OUString implGetDocumentModuleName(const EventHandlerInfo &rInfo, const css::uno::Sequence< css::uno::Any > &rArgs) const override
css::uno::Any createRange(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Range object (the argument must contain a UNO range or UNO range list).
bool isSelectionChanged(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex)
Checks if selection has been changed compared to selection of last call.
virtual css::uno::Sequence< css::uno::Any > implBuildArgumentList(const EventHandlerInfo &rInfo, const css::uno::Sequence< css::uno::Any > &rArgs) override
virtual bool implPrepareEvent(EventQueue &rEventQueue, const EventHandlerInfo &rInfo, const css::uno::Sequence< css::uno::Any > &rArgs) override
ScVbaEventsHelper(const css::uno::Sequence< css::uno::Any > &rArgs)
css::uno::Any maOldSelection
css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
static vcl::Window * GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
::std::deque< EventQueueEntry > EventQueue
void processVbaEventNoThrow(sal_Int32 nEventId, const css::uno::Sequence< css::uno::Any > &rArgs)
virtual void SAL_CALL notifyEvent(const css::document::EventObject &rEvent) override
void registerEventHandler(sal_Int32 nEventId, sal_Int32 nModuleType, const char *pcMacroName, sal_Int32 nCancelIndex=-1, const css::uno::Any &rUserData=css::uno::Any())
css::uno::Reference< css::frame::XModel > mxModel
SfxObjectShell * mpShell
static void checkArgument(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex)
bool isDisposed() const
PointerState GetPointerState()
int nCount
#define MOUSE_LEFT
#define MOUSE_MIDDLE
#define MOUSE_RIGHT
sal_Int32 nIndex
void * p
const char * sName
uno::Reference< XHelperInterface > getUnoSheetModuleObj(const uno::Reference< table::XCellRange > &xRange)
VBAHELPER_DLLPUBLIC css::uno::Reference< XHelperInterface > getVBADocument(const css::uno::Reference< css::frame::XModel > &xModel)
css::uno::Reference< css::uno::XInterface > createVBAUnoAPIServiceWithArgs(SfxObjectShell const *pShell, const char *_pAsciiName, const css::uno::Sequence< css::uno::Any > &aArgs)
Reference< XController > xController
Reference< XFrame > xFrame
Reference< XModel > xModel
sal_Int16 SCTAB
Definition: types.hxx:22
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * ScVbaEventsHelper_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &arguments)
IMPL_LINK(ScVbaEventListener, processWindowResizeEvent, void *, p, void)
sal_Int32 nLength