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