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 <unotools/eventcfg.hxx>
41 #include <toolkit/helper/vclunohelper.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