LibreOffice Module sc (master)  1
vbaeventshelper.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "vbaeventshelper.hxx"
21 #include "excelvbahelper.hxx"
22 
23 #include <com/sun/star/awt/XTopWindow.hpp>
24 #include <com/sun/star/awt/XTopWindowListener.hpp>
25 #include <com/sun/star/awt/XWindowListener.hpp>
26 #include <com/sun/star/frame/XBorderResizeListener.hpp>
27 #include <com/sun/star/frame/XControllerBorder.hpp>
28 #include <com/sun/star/script/ModuleType.hpp>
29 #include <com/sun/star/script/vba/VBAEventId.hpp>
30 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
31 #include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
32 #include <com/sun/star/table/XCellRange.hpp>
33 #include <com/sun/star/util/XChangesListener.hpp>
34 #include <com/sun/star/util/XChangesNotifier.hpp>
35 
36 #include <cppuhelper/implbase.hxx>
38 #include <unotools/eventcfg.hxx>
39 #include <vcl/event.hxx>
40 #include <vcl/svapp.hxx>
41 #include <vcl/window.hxx>
43 
44 #include <docsh.hxx>
45 #include <document.hxx>
46 #include <cellsuno.hxx>
47 #include <convuno.hxx>
48 #include "vbaapplication.hxx"
49 
50 using namespace ::com::sun::star;
51 using namespace ::com::sun::star::script::vba::VBAEventId;
52 using namespace ::ooo::vba;
53 
54 namespace {
55 
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 
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 
129  void startControllerListening( const uno::Reference< frame::XController >& rxController );
131  void stopControllerListening( const uno::Reference< frame::XController >& rxController );
132 
133  // XTopWindowListener
134  virtual void SAL_CALL windowOpened( const lang::EventObject& rEvent ) override;
135  virtual void SAL_CALL windowClosing( const lang::EventObject& rEvent ) override;
136  virtual void SAL_CALL windowClosed( const lang::EventObject& rEvent ) override;
137  virtual void SAL_CALL windowMinimized( const lang::EventObject& rEvent ) override;
138  virtual void SAL_CALL windowNormalized( const lang::EventObject& rEvent ) override;
139  virtual void SAL_CALL windowActivated( const lang::EventObject& rEvent ) override;
140  virtual void SAL_CALL windowDeactivated( const lang::EventObject& rEvent ) override;
141 
142  // XWindowListener
143  virtual void SAL_CALL windowResized( const awt::WindowEvent& rEvent ) override;
144  virtual void SAL_CALL windowMoved( const awt::WindowEvent& rEvent ) override;
145  virtual void SAL_CALL windowShown( const lang::EventObject& rEvent ) override;
146  virtual void SAL_CALL windowHidden( const lang::EventObject& rEvent ) override;
147 
148  // XBorderResizeListener
149  virtual void SAL_CALL borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& aNewSize ) override;
150 
151  // XChangesListener
152  virtual void SAL_CALL changesOccurred( const util::ChangesEvent& rEvent ) override;
153 
154  // XEventListener
155  virtual void SAL_CALL disposing( const lang::EventObject& rEvent ) override;
156 
157 private:
159  void startModelListening();
161  void stopModelListening();
162 
164  uno::Reference< frame::XController > getControllerForWindow( vcl::Window* pWindow ) const;
165 
167  void processWindowActivateEvent( vcl::Window* pWindow, bool bActivate );
169  void postWindowResizeEvent( vcl::Window* pWindow );
171  DECL_LINK( processWindowResizeEvent, void*, void );
172 
173 private:
174  typedef ::std::map< VclPtr<vcl::Window>, uno::Reference< frame::XController > > WindowControllerMap;
175 
176  ::osl::Mutex maMutex;
178  uno::Reference< frame::XModel > mxModel;
180  WindowControllerMap maControllers;
181  std::multiset< VclPtr<vcl::Window> > m_PostedWindows;
186 };
187 
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 
201  try
202  {
203  uno::Reference< frame::XController > xController( mxModel->getCurrentController(), uno::UNO_SET_THROW );
205  }
206  catch( uno::Exception& )
207  {
208  }
209 }
210 
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 
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 
257 void SAL_CALL ScVbaEventListener::windowOpened( const lang::EventObject& /*rEvent*/ )
258 {
259 }
260 
261 void SAL_CALL ScVbaEventListener::windowClosing( const lang::EventObject& /*rEvent*/ )
262 {
263 }
264 
265 void SAL_CALL ScVbaEventListener::windowClosed( const lang::EventObject& /*rEvent*/ )
266 {
267 }
268 
269 void SAL_CALL ScVbaEventListener::windowMinimized( const lang::EventObject& /*rEvent*/ )
270 {
271 }
272 
273 void SAL_CALL ScVbaEventListener::windowNormalized( const lang::EventObject& /*rEvent*/ )
274 {
275 }
276 
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 )
291  // fire activation event for the new window
292  processWindowActivateEvent( pWindow, true );
293  mpActiveWindow = pWindow;
294  }
295  }
296 }
297 
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 
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 );
323  }
324 }
325 
326 void SAL_CALL ScVbaEventListener::windowMoved( const awt::WindowEvent& /*rEvent*/ )
327 {
328 }
329 
330 void SAL_CALL ScVbaEventListener::windowShown( const lang::EventObject& /*rEvent*/ )
331 {
332 }
333 
334 void SAL_CALL ScVbaEventListener::windowHidden( const lang::EventObject& /*rEvent*/ )
335 {
336 }
337 
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 );
348  }
349 }
350 
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 
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" );
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 
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 
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 
452 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 
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 
470 {
471  // check that the passed window is still alive (it must be registered in maControllers)
472  if( pWindow && (maControllers.count( pWindow ) > 0) )
473  {
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 
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 
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 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 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 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 
572 {
573 }
574 
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 && xController.is() )
624  mxListener->startControllerListening( xController );
625  }
627 }
628 
630 {
631  return "ScVbaEventsHelper";
632 }
633 
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 
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 
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 
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() )
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 
802 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 
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 
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 
854 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 
861 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 
888 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 
898 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 *
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: */
void startControllerListening(const uno::Reference< frame::XController > &rxController)
Starts listening to the passed document controller.
bool mbWindowResized
Currently activated window, to prevent multiple (de)activation.
ScVbaEventsHelper & mrVbaEvents
virtual void SAL_CALL notifyEvent(const css::document::EventObject &rEvent) override
void stopModelListening()
Stops listening to the document model.
css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual void SAL_CALL windowActivated(const lang::EventObject &rEvent) override
Reference< XFrame > xFrame
static VclPtr< vcl::Window > GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
void stopControllerListening(const uno::Reference< frame::XController > &rxController)
Stops listening to the passed document controller.
DECL_LINK(processWindowResizeEvent, void *, void)
Callback link for Application::PostUserEvent().
virtual void SAL_CALL changesOccurred(const util::ChangesEvent &rEvent) override
void postWindowResizeEvent(vcl::Window *pWindow)
Posts a Workbook_WindowResize user event.
css::uno::Reference< css::frame::XModel2 > mxModel
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
::std::map< VclPtr< vcl::Window >, uno::Reference< frame::XController > > WindowControllerMap
WindowControllerMap maControllers
virtual OUString implGetDocumentModuleName(const EventHandlerInfo &rInfo, const css::uno::Sequence< css::uno::Any > &rArgs) const override
virtual void SAL_CALL windowMinimized(const lang::EventObject &rEvent) override
IMPL_LINK(ScVbaEventListener, processWindowResizeEvent, void *, p, void)
VclPtr< vcl::Window > mpActiveWindow
Keeps processWindowResizeEvent windows from being deleted between postWindowResizeEvent and processWi...
virtual bool implPrepareEvent(EventQueue &rEventQueue, const EventHandlerInfo &rInfo, const css::uno::Sequence< css::uno::Any > &rArgs) override
virtual ~ScVbaEventsHelper() override
css::uno::Reference< css::frame::XModel > mxModel
Reference< XController > xController
uno::Reference< XHelperInterface > getUnoSheetModuleObj(const uno::Reference< table::XCellRange > &xRange)
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
int nCount
bool isSelectionChanged(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex)
Checks if selection has been changed compared to selection of last call.
css::uno::Any createWorksheet(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Worksheet object (the argument must contain a sheet index).
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1144
css::uno::Reference< css::uno::XInterface > createVBAUnoAPIServiceWithArgs(SfxObjectShell const *pShell, const char *_pAsciiName, const css::uno::Sequence< css::uno::Any > &aArgs)
const char * sName
#define MOUSE_LEFT
::osl::Mutex maMutex
bool empty() const
Definition: rangelst.hxx:89
virtual void SAL_CALL windowResized(const awt::WindowEvent &rEvent) override
ScDocShell * mpDocShell
bool mbDisposed
True = borders changed system event processed.
static bool getDocumentEventsEnabled()
Returns true, if VBA document events are enabled.
ScVbaEventsHelper(const css::uno::Sequence< css::uno::Any > &rArgs)
virtual void SAL_CALL borderWidthsChanged(const uno::Reference< uno::XInterface > &rSource, const frame::BorderWidths &aNewSize) override
static OUString GetEventName(GlobalEventId nID)
virtual void SAL_CALL windowOpened(const lang::EventObject &rEvent) override
SfxObjectShell * mpShell
void processVbaEventNoThrow(sal_Int32 nEventId, const css::uno::Sequence< css::uno::Any > &rArgs)
static void checkArgument(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex)
virtual void SAL_CALL notifyEvent(const css::document::EventObject &rEvent) override
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * ScVbaEventsHelper_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &arguments)
uno::Reference< frame::XController > getControllerForWindow(vcl::Window *pWindow) const
Returns the controller for the passed VCL window.
ScVbaEventListener(ScVbaEventsHelper &rVbaEvents, const uno::Reference< frame::XModel > &rxModel, ScDocShell *pDocShell)
OUString SAL_CALL getImplementationName() override
virtual void SAL_CALL windowShown(const lang::EventObject &rEvent) override
const SCTAB MAXTAB
Definition: address.hxx:71
virtual void implPostProcessEvent(EventQueue &rEventQueue, const EventHandlerInfo &rInfo, bool bCancel) override
virtual void SAL_CALL windowDeactivated(const lang::EventObject &rEvent) override
css::uno::Any createRange(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Range object (the argument must contain a UNO range or UNO range list).
css::uno::Any maOldSelection
void processWindowActivateEvent(vcl::Window *pWindow, bool bActivate)
Calls the Workbook_Window[Activate|Deactivate] event handler.
virtual css::uno::Sequence< css::uno::Any > implBuildArgumentList(const EventHandlerInfo &rInfo, const css::uno::Sequence< css::uno::Any > &rArgs) override
uno::Reference< XHelperInterface > getVBADocument(const uno::Reference< frame::XModel > &xModel)
void registerEventHandler(sal_Int32 nEventId, sal_Int32 nModuleType, const char *pcMacroName, sal_Int32 nCancelIndex=-1, const css::uno::Any &rUserData=css::uno::Any())
uno::Reference< frame::XModel > mxModel
virtual void SAL_CALL disposing(const lang::EventObject &rEvent) override
virtual void SAL_CALL windowNormalized(const lang::EventObject &rEvent) override
void * p
::std::deque< EventQueueEntry > EventQueue
const ScDocument & GetDocument() const
Definition: docsh.hxx:216
virtual void SAL_CALL windowHidden(const lang::EventObject &rEvent) override
bool IsDisposed() const
virtual void SAL_CALL windowMoved(const awt::WindowEvent &rEvent) override
::rtl::Reference< ScVbaEventListener > mxListener
Reference< XModel > xModel
const OUString & GetCodeName() const
Definition: document.hxx:607
sal_Int32 nLength
#define MOUSE_MIDDLE
std::multiset< VclPtr< vcl::Window > > m_PostedWindows
Maps VCL top windows to their controllers.
static void FillScRange(ScRange &rScRange, const css::table::CellRangeAddress &rApiRange)
Definition: convuno.hxx:80
virtual void SAL_CALL windowClosed(const lang::EventObject &rEvent) override
PointerState GetPointerState()
void startModelListening()
Starts listening to the document model.
css::uno::Any createWindow(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Window object (the argument must contain a model controller).
virtual void SAL_CALL windowClosing(const lang::EventObject &rEvent) override
#define MOUSE_RIGHT
css::uno::Any createHyperlink(const css::uno::Sequence< css::uno::Any > &rArgs, sal_Int32 nIndex) const
Creates a VBA Hyperlink object (the argument must contain a UNO cell).
bool mbBorderChanged
True = window resize system event processed.
sal_Int16 SCTAB
Definition: types.hxx:23