1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "vbaeventshelper.hxx"
25 
26 #include <com/sun/star/awt/XTopWindow.hpp>
27 #include <com/sun/star/awt/XTopWindowListener.hpp>
28 #include <com/sun/star/awt/XWindowListener.hpp>
29 #include <com/sun/star/frame/XBorderResizeListener.hpp>
30 #include <com/sun/star/frame/XControllerBorder.hpp>
31 #include <com/sun/star/script/ModuleType.hpp>
32 #include <com/sun/star/script/vba/VBAEventId.hpp>
33 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
34 #include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
35 #include <com/sun/star/table/XCellRange.hpp>
36 #include <com/sun/star/util/XChangesListener.hpp>
37 #include <com/sun/star/util/XChangesNotifier.hpp>
38 
39 #include <cppuhelper/implbase4.hxx>
40 #include <toolkit/unohlp.hxx>
41 #include <unotools/eventcfg.hxx>
42 #include <vbahelper/helperdecl.hxx>
43 #include <vcl/svapp.hxx>
44 #include <vcl/window.hxx>
45 
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 using ::rtl::OUString;
55 
56 // ============================================================================
57 
58 namespace {
59 
60 /** Extracts a sheet index from the specified element of the passed sequence.
61     The element may be an integer, a Calc range or ranges object, or a VBA Range object. */
lclGetTabFromArgs(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex)62 SCTAB lclGetTabFromArgs( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) throw (lang::IllegalArgumentException)
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.getLength() > 0 )
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 0;
115 }
116 
117 } // namespace
118 
119 // ============================================================================
120 
121 typedef ::cppu::WeakImplHelper4< awt::XTopWindowListener, awt::XWindowListener, frame::XBorderResizeListener, util::XChangesListener > ScVbaEventListener_BASE;
122 
123 // This class is to process Workbook window related event
124 class ScVbaEventListener : public ScVbaEventListener_BASE
125 {
126 public :
127     ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell );
128     virtual ~ScVbaEventListener();
129 
130     /** Starts listening to the passed document controller. */
131     void startControllerListening( const uno::Reference< frame::XController >& rxController );
132     /** Stops listening to the passed document controller. */
133     void stopControllerListening( const uno::Reference< frame::XController >& rxController );
134 
135     // XTopWindowListener
136     virtual void SAL_CALL windowOpened( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
137     virtual void SAL_CALL windowClosing( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
138     virtual void SAL_CALL windowClosed( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
139     virtual void SAL_CALL windowMinimized( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
140     virtual void SAL_CALL windowNormalized( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
141     virtual void SAL_CALL windowActivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
142     virtual void SAL_CALL windowDeactivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
143 
144     // XWindowListener
145     virtual void SAL_CALL windowResized( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException);
146     virtual void SAL_CALL windowMoved( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException);
147     virtual void SAL_CALL windowShown( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
148     virtual void SAL_CALL windowHidden( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
149 
150     // XBorderResizeListener
151     virtual void SAL_CALL borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& aNewSize ) throw (uno::RuntimeException);
152 
153     // XChangesListener
154     virtual void SAL_CALL changesOccurred( const util::ChangesEvent& rEvent ) throw (uno::RuntimeException);
155 
156     // XEventListener
157     virtual void SAL_CALL disposing( const lang::EventObject& rEvent ) throw (uno::RuntimeException);
158 
159 private:
160     /** Starts listening to the document model. */
161     void startModelListening();
162     /** Stops listening to the document model. */
163     void stopModelListening();
164 
165     /** Returns the controller for the passed VCL window. */
166     uno::Reference< frame::XController > getControllerForWindow( Window* pWindow ) const;
167 
168     /** Calls the Workbook_Window[Activate|Deactivate] event handler. */
169     void processWindowActivateEvent( Window* pWindow, bool bActivate );
170     /** Posts a Workbook_WindowResize user event. */
171     void postWindowResizeEvent( Window* pWindow );
172     /** Callback link for Application::PostUserEvent(). */
173     DECL_LINK( processWindowResizeEvent, Window* );
174 
175 private:
176     typedef ::std::map< Window*, uno::Reference< frame::XController > > WindowControllerMap;
177 
178     ::osl::Mutex        maMutex;
179     ScVbaEventsHelper&  mrVbaEvents;
180     uno::Reference< frame::XModel > mxModel;
181     ScDocShell*         mpDocShell;
182     WindowControllerMap maControllers;          /// Maps VCL top windows to their controllers.
183     Window*             mpActiveWindow;         /// Currently activated window, to prevent multiple (de)activation.
184     bool                mbWindowResized;        /// True = window resize system event processed.
185     bool                mbBorderChanged;        /// True = borders changed system event processed.
186     bool                mbDisposed;
187 };
188 
189 // ----------------------------------------------------------------------------
190 
ScVbaEventListener(ScVbaEventsHelper & rVbaEvents,const uno::Reference<frame::XModel> & rxModel,ScDocShell * pDocShell)191 ScVbaEventListener::ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell ) :
192     mrVbaEvents( rVbaEvents ),
193     mxModel( rxModel ),
194     mpDocShell( pDocShell ),
195     mpActiveWindow( 0 ),
196     mbWindowResized( false ),
197     mbBorderChanged( false ),
198     mbDisposed( !rxModel.is() )
199 {
200     if( !mxModel.is() )
201         return;
202 
203     startModelListening();
204     try
205     {
206         uno::Reference< frame::XController > xController( mxModel->getCurrentController(), uno::UNO_QUERY_THROW );
207         startControllerListening( xController );
208     }
209     catch( uno::Exception& )
210     {
211     }
212 }
213 
~ScVbaEventListener()214 ScVbaEventListener::~ScVbaEventListener()
215 {
216 }
217 
startControllerListening(const uno::Reference<frame::XController> & rxController)218 void ScVbaEventListener::startControllerListening( const uno::Reference< frame::XController >& rxController )
219 {
220     ::osl::MutexGuard aGuard( maMutex );
221 
222     uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
223     if( xWindow.is() )
224         try { xWindow->addWindowListener( this ); } catch( uno::Exception& ) {}
225 
226     uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
227     if( xTopWindow.is() )
228         try { xTopWindow->addTopWindowListener( this ); } catch( uno::Exception& ) {}
229 
230     uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
231     if( xControllerBorder.is() )
232         try { xControllerBorder->addBorderResizeListener( this ); } catch( uno::Exception& ) {}
233 
234     if( Window* pWindow = VCLUnoHelper::GetWindow( xWindow ) )
235         maControllers[ pWindow ] = rxController;
236 }
237 
stopControllerListening(const uno::Reference<frame::XController> & rxController)238 void ScVbaEventListener::stopControllerListening( const uno::Reference< frame::XController >& rxController )
239 {
240     ::osl::MutexGuard aGuard( maMutex );
241 
242     uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
243     if( xWindow.is() )
244         try { xWindow->removeWindowListener( this ); } catch( uno::Exception& ) {}
245 
246     uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
247     if( xTopWindow.is() )
248         try { xTopWindow->removeTopWindowListener( this ); } catch( uno::Exception& ) {}
249 
250     uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
251     if( xControllerBorder.is() )
252         try { xControllerBorder->removeBorderResizeListener( this ); } catch( uno::Exception& ) {}
253 
254     if( Window* pWindow = VCLUnoHelper::GetWindow( xWindow ) )
255     {
256         maControllers.erase( pWindow );
257         if( pWindow == mpActiveWindow )
258             mpActiveWindow = 0;
259     }
260 }
261 
windowOpened(const lang::EventObject &)262 void SAL_CALL ScVbaEventListener::windowOpened( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
263 {
264 }
265 
windowClosing(const lang::EventObject &)266 void SAL_CALL ScVbaEventListener::windowClosing( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
267 {
268 }
269 
windowClosed(const lang::EventObject &)270 void SAL_CALL ScVbaEventListener::windowClosed( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
271 {
272 }
273 
windowMinimized(const lang::EventObject &)274 void SAL_CALL ScVbaEventListener::windowMinimized( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
275 {
276 }
277 
windowNormalized(const lang::EventObject &)278 void SAL_CALL ScVbaEventListener::windowNormalized( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
279 {
280 }
281 
windowActivated(const lang::EventObject & rEvent)282 void SAL_CALL ScVbaEventListener::windowActivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
283 {
284     ::osl::MutexGuard aGuard( maMutex );
285 
286     if( !mbDisposed )
287     {
288         uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
289         Window* pWindow = VCLUnoHelper::GetWindow( xWindow );
290         OSL_TRACE( "ScVbaEventListener::windowActivated - pWindow = 0x%x, mpActiveWindow = 0x%x", pWindow, mpActiveWindow );
291         // do not fire activation event multiple time for the same window
292         if( pWindow && (pWindow != mpActiveWindow) )
293         {
294             // if another window is active, fire deactivation event first
295             if( mpActiveWindow )
296                 processWindowActivateEvent( mpActiveWindow, false );
297             // fire activation event for the new window
298             processWindowActivateEvent( pWindow, true );
299             mpActiveWindow = pWindow;
300         }
301     }
302 }
303 
windowDeactivated(const lang::EventObject & rEvent)304 void SAL_CALL ScVbaEventListener::windowDeactivated( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
305 {
306     ::osl::MutexGuard aGuard( maMutex );
307 
308     if( !mbDisposed )
309     {
310         uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
311         Window* pWindow = VCLUnoHelper::GetWindow( xWindow );
312         OSL_TRACE( "ScVbaEventListener::windowDeactivated - pWindow = 0x%x, mpActiveWindow = 0x%x", pWindow, mpActiveWindow );
313         // do not fire the deactivation event, if the window is not active (prevent multiple deactivation)
314         if( pWindow && (pWindow == mpActiveWindow) )
315             processWindowActivateEvent( pWindow, false );
316         // forget pointer to the active window
317         mpActiveWindow = 0;
318     }
319 }
320 
windowResized(const awt::WindowEvent & rEvent)321 void SAL_CALL ScVbaEventListener::windowResized( const awt::WindowEvent& rEvent ) throw (uno::RuntimeException)
322 {
323     ::osl::MutexGuard aGuard( maMutex );
324 
325     mbWindowResized = true;
326     if( !mbDisposed && mbBorderChanged )
327     {
328         uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
329         postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
330     }
331 }
332 
windowMoved(const awt::WindowEvent &)333 void SAL_CALL ScVbaEventListener::windowMoved( const awt::WindowEvent& /*rEvent*/ ) throw (uno::RuntimeException)
334 {
335 }
336 
windowShown(const lang::EventObject &)337 void SAL_CALL ScVbaEventListener::windowShown( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
338 {
339 }
340 
windowHidden(const lang::EventObject &)341 void SAL_CALL ScVbaEventListener::windowHidden( const lang::EventObject& /*rEvent*/ ) throw (uno::RuntimeException)
342 {
343 }
344 
borderWidthsChanged(const uno::Reference<uno::XInterface> & rSource,const frame::BorderWidths &)345 void SAL_CALL ScVbaEventListener::borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& /*aNewSize*/ ) throw (uno::RuntimeException)
346 {
347     ::osl::MutexGuard aGuard( maMutex );
348 
349     mbBorderChanged = true;
350     if( !mbDisposed && mbWindowResized )
351     {
352         uno::Reference< frame::XController > xController( rSource, uno::UNO_QUERY );
353         uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( xController );
354         postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
355     }
356 }
357 
changesOccurred(const util::ChangesEvent & rEvent)358 void SAL_CALL ScVbaEventListener::changesOccurred( const util::ChangesEvent& rEvent ) throw (uno::RuntimeException)
359 {
360     ::osl::MutexGuard aGuard( maMutex );
361 
362     sal_Int32 nCount = rEvent.Changes.getLength();
363     if( mbDisposed || !mpDocShell || (nCount == 0) )
364         return;
365 
366     util::ElementChange aChange = rEvent.Changes[ 0 ];
367     OUString sOperation;
368     aChange.Accessor >>= sOperation;
369     if( !sOperation.equalsIgnoreAsciiCaseAscii("cell-change") )
370         return;
371 
372     if( nCount == 1 )
373     {
374         uno::Reference< table::XCellRange > xRangeObj;
375         aChange.ReplacedElement >>= xRangeObj;
376         if( xRangeObj.is() )
377         {
378             uno::Sequence< uno::Any > aArgs( 1 );
379             aArgs[0] <<= xRangeObj;
380             mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
381         }
382         return;
383     }
384 
385     ScRangeList aRangeList;
386     for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
387     {
388         aChange = rEvent.Changes[ nIndex ];
389         aChange.Accessor >>= sOperation;
390         uno::Reference< table::XCellRange > xRangeObj;
391         aChange.ReplacedElement >>= xRangeObj;
392         if( xRangeObj.is() && sOperation.equalsIgnoreAsciiCaseAscii("cell-change") )
393         {
394             uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable( xRangeObj, uno::UNO_QUERY );
395             if( xCellRangeAddressable.is() )
396             {
397                 ScRange aRange;
398                 ScUnoConversion::FillScRange( aRange, xCellRangeAddressable->getRangeAddress() );
399                 aRangeList.Append( aRange );
400             }
401         }
402     }
403 
404     if( aRangeList.Count() > 0 )
405     {
406         uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( mpDocShell, aRangeList ) );
407         uno::Sequence< uno::Any > aArgs(1);
408         aArgs[0] <<= xRanges;
409         mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
410     }
411 }
412 
disposing(const lang::EventObject & rEvent)413 void SAL_CALL ScVbaEventListener::disposing( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
414 {
415     ::osl::MutexGuard aGuard( maMutex );
416 
417     uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
418     if( xModel.is() )
419     {
420         OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
421         stopModelListening();
422         mbDisposed = true;
423         return;
424     }
425 
426     uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
427     if( xController.is() )
428     {
429         stopControllerListening( xController );
430         return;
431     }
432 }
433 
434 // private --------------------------------------------------------------------
435 
startModelListening()436 void ScVbaEventListener::startModelListening()
437 {
438     try
439     {
440         uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
441         xChangesNotifier->addChangesListener( this );
442     }
443     catch( uno::Exception& )
444     {
445     }
446 }
447 
stopModelListening()448 void ScVbaEventListener::stopModelListening()
449 {
450     try
451     {
452         uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
453         xChangesNotifier->removeChangesListener( this );
454     }
455     catch( uno::Exception& )
456     {
457     }
458 }
459 
getControllerForWindow(Window * pWindow) const460 uno::Reference< frame::XController > ScVbaEventListener::getControllerForWindow( Window* pWindow ) const
461 {
462     WindowControllerMap::const_iterator aIt = maControllers.find( pWindow );
463     return (aIt == maControllers.end()) ? uno::Reference< frame::XController >() : aIt->second;
464 }
465 
processWindowActivateEvent(Window * pWindow,bool bActivate)466 void ScVbaEventListener::processWindowActivateEvent( Window* pWindow, bool bActivate )
467 {
468     uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
469     if( xController.is() )
470     {
471         uno::Sequence< uno::Any > aArgs( 1 );
472         aArgs[ 0 ] <<= xController;
473         mrVbaEvents.processVbaEventNoThrow( bActivate ? WORKBOOK_WINDOWACTIVATE : WORKBOOK_WINDOWDEACTIVATE, aArgs );
474     }
475 }
476 
postWindowResizeEvent(Window * pWindow)477 void ScVbaEventListener::postWindowResizeEvent( Window* pWindow )
478 {
479     // check that the passed window is still alive (it must be registered in maControllers)
480     if( pWindow && (maControllers.count( pWindow ) > 0) )
481     {
482         mbWindowResized = mbBorderChanged = false;
483         acquire();  // ensure we don't get deleted before the timer fires
484         Application::PostUserEvent( LINK( this, ScVbaEventListener, processWindowResizeEvent ), pWindow );
485     }
486 }
487 
IMPL_LINK(ScVbaEventListener,processWindowResizeEvent,Window *,EMPTYARG pWindow)488 IMPL_LINK( ScVbaEventListener, processWindowResizeEvent, Window*, EMPTYARG pWindow )
489 {
490     ::osl::MutexGuard aGuard( maMutex );
491 
492     /*  Check that the passed window is still alive (it must be registered in
493         maControllers). While closing a document, postWindowResizeEvent() may
494         be called on the last window which posts a user event via
495         Application::PostUserEvent to call this event handler. VCL will trigger
496         the handler some time later. Sometimes, the window gets deleted before.
497         This is handled via the disposing() function which removes the window
498         pointer from the member maControllers. Thus, checking whether
499         maControllers contains pWindow ensures that the window is still alive. */
500     if( !mbDisposed && pWindow && (maControllers.count( pWindow ) > 0) )
501     {
502         // do not fire event unless all mouse buttons have been released
503         Window::PointerState aPointerState = pWindow->GetPointerState();
504         if( (aPointerState.mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0 )
505         {
506             uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
507             if( xController.is() )
508             {
509                 uno::Sequence< uno::Any > aArgs( 1 );
510                 aArgs[ 0 ] <<= xController;
511                 // #163419# do not throw exceptions into application core
512                 mrVbaEvents.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE, aArgs );
513             }
514         }
515     }
516     release();
517     return 0;
518 }
519 
520 // ============================================================================
521 
ScVbaEventsHelper(const uno::Sequence<uno::Any> & rArgs,const uno::Reference<uno::XComponentContext> & xContext)522 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs, const uno::Reference< uno::XComponentContext >& xContext ) :
523     VbaEventsHelperBase( rArgs, xContext ),
524     mbOpened( false )
525 {
526     mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
527     mpDoc = mpDocShell ? mpDocShell->GetDocument() : 0;
528 
529     if( !mxModel.is() || !mpDocShell || !mpDoc )
530         return;
531 
532 #define REGISTER_EVENT( eventid, moduletype, classname, eventname, cancelindex, worksheet ) \
533     registerEventHandler( eventid, moduletype, classname "_" eventname, cancelindex, uno::Any( worksheet ) )
534 #define REGISTER_AUTO_EVENT( eventid, eventname ) \
535     REGISTER_EVENT( AUTO_##eventid, script::ModuleType::NORMAL, "Auto", eventname, -1, false )
536 #define REGISTER_WORKBOOK_EVENT( eventid, eventname, cancelindex ) \
537     REGISTER_EVENT( WORKBOOK_##eventid, script::ModuleType::DOCUMENT, "Workbook", eventname, cancelindex, false )
538 #define REGISTER_WORKSHEET_EVENT( eventid, eventname, cancelindex ) \
539     REGISTER_EVENT( WORKSHEET_##eventid, script::ModuleType::DOCUMENT, "Worksheet", eventname, cancelindex, true ); \
540     REGISTER_EVENT( (USERDEFINED_START + WORKSHEET_##eventid), script::ModuleType::DOCUMENT, "Workbook", "Sheet" eventname, (((cancelindex) >= 0) ? ((cancelindex) + 1) : -1), false )
541 
542     // global
543     REGISTER_AUTO_EVENT( OPEN,  "Open" );
544     REGISTER_AUTO_EVENT( CLOSE, "Close" );
545 
546     // Workbook
547     REGISTER_WORKBOOK_EVENT( ACTIVATE,            "Activate",           -1 );
548     REGISTER_WORKBOOK_EVENT( DEACTIVATE,          "Deactivate",         -1 );
549     REGISTER_WORKBOOK_EVENT( OPEN,                "Open",               -1 );
550     REGISTER_WORKBOOK_EVENT( BEFORECLOSE,         "BeforeClose",        0 );
551     REGISTER_WORKBOOK_EVENT( BEFOREPRINT,         "BeforePrint",        0 );
552     REGISTER_WORKBOOK_EVENT( BEFORESAVE,          "BeforeSave",         1 );
553     REGISTER_WORKBOOK_EVENT( AFTERSAVE,           "AfterSave",          -1 );
554     REGISTER_WORKBOOK_EVENT( NEWSHEET,            "NewSheet",           -1 );
555     REGISTER_WORKBOOK_EVENT( WINDOWACTIVATE,      "WindowActivate",     -1 );
556     REGISTER_WORKBOOK_EVENT( WINDOWDEACTIVATE,    "WindowDeactivate",   -1 );
557     REGISTER_WORKBOOK_EVENT( WINDOWRESIZE,        "WindowResize",       -1 );
558 
559     // Worksheet events. All events have a corresponding workbook event.
560     REGISTER_WORKSHEET_EVENT( ACTIVATE,           "Activate",           -1 );
561     REGISTER_WORKSHEET_EVENT( DEACTIVATE,         "Deactivate",         -1 );
562     REGISTER_WORKSHEET_EVENT( BEFOREDOUBLECLICK,  "BeforeDoubleClick",  1 );
563     REGISTER_WORKSHEET_EVENT( BEFORERIGHTCLICK,   "BeforeRightClick",   1 );
564     REGISTER_WORKSHEET_EVENT( CALCULATE,          "Calculate",          -1 );
565     REGISTER_WORKSHEET_EVENT( CHANGE,             "Change",             -1 );
566     REGISTER_WORKSHEET_EVENT( SELECTIONCHANGE,    "SelectionChange",    -1 );
567     REGISTER_WORKSHEET_EVENT( FOLLOWHYPERLINK,    "FollowHyperlink",    -1 );
568 
569 #undef REGISTER_WORKSHEET_EVENT
570 #undef REGISTER_WORKBOOK_EVENT
571 #undef REGISTER_AUTO_EVENT
572 #undef REGISTER_EVENT
573 }
574 
~ScVbaEventsHelper()575 ScVbaEventsHelper::~ScVbaEventsHelper()
576 {
577 }
578 
notifyEvent(const css::document::EventObject & rEvent)579 void SAL_CALL ScVbaEventsHelper::notifyEvent( const css::document::EventObject& rEvent ) throw (css::uno::RuntimeException)
580 {
581     static const uno::Sequence< uno::Any > saEmptyArgs;
582     if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_OPENDOC )) ||
583         (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CREATEDOC )) )  // CREATEDOC triggered e.g. during VBA Workbooks.Add
584     {
585         processVbaEventNoThrow( WORKBOOK_OPEN, saEmptyArgs );
586     }
587     else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_ACTIVATEDOC ) )
588     {
589         processVbaEventNoThrow( WORKBOOK_ACTIVATE, saEmptyArgs );
590     }
591     else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_DEACTIVATEDOC ) )
592     {
593         processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
594     }
595     else if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEDOCDONE )) ||
596              (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEASDOCDONE )) ||
597              (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVETODOCDONE )) )
598     {
599         uno::Sequence< uno::Any > aArgs( 1 );
600         aArgs[ 0 ] <<= true;
601         processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
602     }
603     else if( (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEDOCFAILED )) ||
604              (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVEASDOCFAILED )) ||
605              (rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_SAVETODOCFAILED )) )
606     {
607         uno::Sequence< uno::Any > aArgs( 1 );
608         aArgs[ 0 ] <<= false;
609         processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
610     }
611     else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CLOSEDOC ) )
612     {
613         /*  Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
614             events and stop listening to the model (done in base class). */
615         uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
616         if( xController.is() )
617         {
618             uno::Sequence< uno::Any > aArgs( 1 );
619             aArgs[ 0 ] <<= xController;
620             processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE, aArgs );
621         }
622         processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
623     }
624     else if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_VIEWCREATED ) )
625     {
626         uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
627         if( mxListener.get() && xController.is() )
628             mxListener->startControllerListening( xController );
629     }
630     VbaEventsHelperBase::notifyEvent( rEvent );
631 }
632 
633 // protected ------------------------------------------------------------------
634 
implPrepareEvent(EventQueue & rEventQueue,const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs)635 bool ScVbaEventsHelper::implPrepareEvent( EventQueue& rEventQueue,
636         const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& rArgs ) throw (uno::RuntimeException)
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.push_back( WORKBOOK_ACTIVATE );
660             uno::Sequence< uno::Any > aArgs( 1 );
661             aArgs[ 0 ] <<= mxModel->getCurrentController();
662             rEventQueue.push_back( EventQueueEntry( WORKBOOK_WINDOWACTIVATE, aArgs ) );
663             rEventQueue.push_back( AUTO_OPEN );
664             // remember initial selection
665             maOldSelection <<= mxModel->getCurrentSelection();
666         }
667         break;
668         case WORKSHEET_SELECTIONCHANGE:
669             // if selection is not changed, then do not fire the event
670             bExecuteEvent = isSelectionChanged( rArgs, 0 );
671         break;
672     }
673 
674     if( bExecuteEvent )
675     {
676         // add workbook event associated to a sheet event
677         bool bSheetEvent = false;
678         if( (rInfo.maUserData >>= bSheetEvent) && bSheetEvent )
679             rEventQueue.push_back( EventQueueEntry( rInfo.mnEventId + USERDEFINED_START, rArgs ) );
680     }
681 
682     return bExecuteEvent;
683 }
684 
implBuildArgumentList(const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs)685 uno::Sequence< uno::Any > ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& rInfo,
686         const uno::Sequence< uno::Any >& rArgs ) throw (lang::IllegalArgumentException)
687 {
688     // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
689     bool bSheetEventAsBookEvent = rInfo.mnEventId > USERDEFINED_START;
690     sal_Int32 nEventId = bSheetEventAsBookEvent ? (rInfo.mnEventId - USERDEFINED_START) : rInfo.mnEventId;
691 
692     uno::Sequence< uno::Any > aVbaArgs;
693     switch( nEventId )
694     {
695         // *** Workbook ***
696 
697         // no arguments
698         case WORKBOOK_ACTIVATE:
699         case WORKBOOK_DEACTIVATE:
700         case WORKBOOK_OPEN:
701         break;
702         // 1 arg: cancel
703         case WORKBOOK_BEFORECLOSE:
704         case WORKBOOK_BEFOREPRINT:
705             aVbaArgs.realloc( 1 );
706             // current cancel state will be inserted by caller
707         break;
708         // 2 args: saveAs, cancel
709         case WORKBOOK_BEFORESAVE:
710             aVbaArgs.realloc( 2 );
711             checkArgumentType< bool >( rArgs, 0 );
712             aVbaArgs[ 0 ] = rArgs[ 0 ];
713             // current cancel state will be inserted by caller
714         break;
715         // 1 arg: success
716         case WORKBOOK_AFTERSAVE:
717             aVbaArgs.realloc( 1 );
718             checkArgumentType< bool >( rArgs, 0 );
719             aVbaArgs[ 0 ] = rArgs[ 0 ];
720         break;
721         // 1 arg: window
722         case WORKBOOK_WINDOWACTIVATE:
723         case WORKBOOK_WINDOWDEACTIVATE:
724         case WORKBOOK_WINDOWRESIZE:
725             aVbaArgs.realloc( 1 );
726             aVbaArgs[ 0 ] = createWindow( rArgs, 0 );
727         break;
728         // 1 arg: worksheet
729         case WORKBOOK_NEWSHEET:
730             aVbaArgs.realloc( 1 );
731             aVbaArgs[ 0 ] = createWorksheet( rArgs, 0 );
732         break;
733 
734         // *** Worksheet ***
735 
736         // no arguments
737         case WORKSHEET_ACTIVATE:
738         case WORKSHEET_CALCULATE:
739         case WORKSHEET_DEACTIVATE:
740         break;
741         // 1 arg: range
742         case WORKSHEET_CHANGE:
743         case WORKSHEET_SELECTIONCHANGE:
744             aVbaArgs.realloc( 1 );
745             aVbaArgs[ 0 ] = createRange( rArgs, 0 );
746         break;
747         // 2 args: range, cancel
748         case WORKSHEET_BEFOREDOUBLECLICK:
749         case WORKSHEET_BEFORERIGHTCLICK:
750             aVbaArgs.realloc( 2 );
751             aVbaArgs[ 0 ] = createRange( rArgs, 0 );
752             // current cancel state will be inserted by caller
753         break;
754         // 1 arg: hyperlink
755         case WORKSHEET_FOLLOWHYPERLINK:
756             aVbaArgs.realloc( 1 );
757             aVbaArgs[ 0 ] = createHyperlink( rArgs, 0 );
758         break;
759     }
760 
761     /*  For workbook events associated to sheet events, the workbook event gets
762         the same arguments but with a Worksheet object in front of them. */
763     if( bSheetEventAsBookEvent )
764     {
765         sal_Int32 nLength = aVbaArgs.getLength();
766         uno::Sequence< uno::Any > aVbaArgs2( nLength + 1 );
767         aVbaArgs2[ 0 ] = createWorksheet( rArgs, 0 );
768         for( sal_Int32 nIndex = 0; nIndex < nLength; ++nIndex )
769             aVbaArgs2[ nIndex + 1 ] = aVbaArgs[ nIndex ];
770         aVbaArgs = aVbaArgs2;
771     }
772 
773     return aVbaArgs;
774 }
775 
implPostProcessEvent(EventQueue & rEventQueue,const EventHandlerInfo & rInfo,bool bCancel)776 void ScVbaEventsHelper::implPostProcessEvent( EventQueue& rEventQueue,
777         const EventHandlerInfo& rInfo, bool bCancel ) throw (uno::RuntimeException)
778 {
779     switch( rInfo.mnEventId )
780     {
781         case WORKBOOK_OPEN:
782             mbOpened = true;
783             // register the listeners
784             if( !mxListener.is() )
785                 mxListener = new ScVbaEventListener( *this, mxModel, mpDocShell );
786         break;
787         case WORKBOOK_BEFORECLOSE:
788             /*  Execute Auto_Close only if not cancelled by event handler, but
789                 before UI asks user whether to cancel closing the document. */
790             if( !bCancel )
791                 rEventQueue.push_back( AUTO_CLOSE );
792         break;
793     }
794 }
795 
implGetDocumentModuleName(const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs) const796 OUString ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& rInfo,
797         const uno::Sequence< uno::Any >& rArgs ) const throw (lang::IllegalArgumentException)
798 {
799     bool bSheetEvent = false;
800     rInfo.maUserData >>= bSheetEvent;
801     SCTAB nTab = bSheetEvent ? lclGetTabFromArgs( rArgs, 0 ) : -1;
802     if( bSheetEvent && (nTab < 0) )
803         throw lang::IllegalArgumentException();
804 
805     String aCodeName;
806     if( bSheetEvent )
807         mpDoc->GetCodeName( nTab, aCodeName );
808     else
809         aCodeName = mpDoc->GetCodeName();
810     return aCodeName;
811 }
812 
813 // private --------------------------------------------------------------------
814 
815 namespace {
816 
817 /** Compares the passed range lists representing sheet selections. Ignores
818     selections that refer to different sheets (returns false in this case). */
lclSelectionChanged(const ScRangeList & rLeft,const ScRangeList & rRight)819 bool lclSelectionChanged( const ScRangeList& rLeft, const ScRangeList& rRight )
820 {
821     // one of the range lists empty? -> return false, if both lists empty
822     bool bLeftEmpty = rLeft.Count() == 0;
823     bool bRightEmpty = rRight.Count() == 0;
824     if( bLeftEmpty || bRightEmpty )
825         return !(bLeftEmpty && bRightEmpty);
826 
827     // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
828     if( rLeft.GetObject( 0 )->aStart.Tab() != rRight.GetObject( 0 )->aStart.Tab() )
829         return false;
830 
831     // compare all ranges
832     return rLeft != rRight;
833 }
834 
835 } // namespace
836 
isSelectionChanged(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex)837 bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) throw (lang::IllegalArgumentException, uno::RuntimeException)
838 {
839     uno::Reference< uno::XInterface > xOldSelection( maOldSelection, uno::UNO_QUERY );
840     uno::Reference< uno::XInterface > xNewSelection = getXSomethingFromArgs< uno::XInterface >( rArgs, nIndex, false );
841     ScCellRangesBase* pOldCellRanges = ScCellRangesBase::getImplementation( xOldSelection );
842     ScCellRangesBase* pNewCellRanges = ScCellRangesBase::getImplementation( xNewSelection );
843     bool bChanged = !pOldCellRanges || !pNewCellRanges || lclSelectionChanged( pOldCellRanges->GetRangeList(), pNewCellRanges->GetRangeList() );
844     maOldSelection <<= xNewSelection;
845     return bChanged;
846 }
847 
createWorksheet(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const848 uno::Any ScVbaEventsHelper::createWorksheet( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
849         throw (lang::IllegalArgumentException, uno::RuntimeException)
850 {
851     // extract sheet index, will throw, if parameter is invalid
852     SCTAB nTab = lclGetTabFromArgs( rArgs, nIndex );
853     return uno::Any( excel::getUnoSheetModuleObj( mxModel, nTab ) );
854 }
855 
createRange(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const856 uno::Any ScVbaEventsHelper::createRange( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
857         throw (lang::IllegalArgumentException, uno::RuntimeException)
858 {
859     // it is possible to pass an existing VBA Range object
860     uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
861     if( !xVbaRange.is() )
862     {
863         uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
864         uno::Reference< table::XCellRange > xRange = getXSomethingFromArgs< table::XCellRange >( rArgs, nIndex );
865         if ( !xRanges.is() && !xRange.is() )
866             throw lang::IllegalArgumentException();
867 
868         uno::Sequence< uno::Any > aArgs( 2 );
869         if ( xRanges.is() )
870         {
871             aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRanges );
872             aArgs[ 1 ] <<= xRanges;
873         }
874         else
875         {
876             aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xRange );
877             aArgs[ 1 ] <<= xRange;
878         }
879         xVbaRange.set( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Range", aArgs ), uno::UNO_QUERY_THROW );
880     }
881     return uno::Any( xVbaRange );
882 }
883 
createHyperlink(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const884 uno::Any ScVbaEventsHelper::createHyperlink( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
885         throw (lang::IllegalArgumentException, uno::RuntimeException)
886 {
887     uno::Reference< table::XCell > xCell = getXSomethingFromArgs< table::XCell >( rArgs, nIndex, false );
888     uno::Sequence< uno::Any > aArgs( 2 );
889     aArgs[ 0 ] <<= excel::getUnoSheetModuleObj( xCell );
890     aArgs[ 1 ] <<= xCell;
891     uno::Reference< uno::XInterface > xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Hyperlink", aArgs ), uno::UNO_SET_THROW );
892     return uno::Any( xHyperlink );
893 }
894 
createWindow(const uno::Sequence<uno::Any> & rArgs,sal_Int32 nIndex) const895 uno::Any ScVbaEventsHelper::createWindow( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
896         throw (lang::IllegalArgumentException, uno::RuntimeException)
897 {
898     uno::Sequence< uno::Any > aArgs( 3 );
899     aArgs[ 0 ] <<= getVBADocument( mxModel );
900     aArgs[ 1 ] <<= mxModel;
901     aArgs[ 2 ] <<= getXSomethingFromArgs< frame::XController >( rArgs, nIndex, false );
902     uno::Reference< uno::XInterface > xWindow( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Window", aArgs ), uno::UNO_SET_THROW );
903     return uno::Any( xWindow );
904 }
905 
906 // ============================================================================
907 
908 namespace vbaeventshelper
909 {
910 namespace sdecl = comphelper::service_decl;
911 sdecl::class_<ScVbaEventsHelper, sdecl::with_args<true> > serviceImpl;
912 extern sdecl::ServiceDecl const serviceDecl(
913     serviceImpl,
914     "ScVbaEventsHelper",
915     "com.sun.star.script.vba.VBASpreadsheetEventProcessor" );
916 }
917 
918 // ============================================================================
919