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  return;
283 
284  uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
285  VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
286  // do not fire activation event multiple time for the same window
287  if( pWindow && (pWindow != mpActiveWindow) )
288  {
289  // if another window is active, fire deactivation event first
290  if( mpActiveWindow )
292  // fire activation event for the new window
293  processWindowActivateEvent( pWindow, true );
294  mpActiveWindow = pWindow;
295  }
296 }
297 
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{ uno::Any(xRangeObj) };
372  mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
373  }
374  return;
375  }
376 
377  ScRangeList aRangeList;
378  for( const util::ElementChange& rChange : rEvent.Changes )
379  {
380  rChange.Accessor >>= sOperation;
381  uno::Reference< table::XCellRange > xRangeObj;
382  rChange.ReplacedElement >>= xRangeObj;
383  if( xRangeObj.is() && sOperation.equalsIgnoreAsciiCase("cell-change") )
384  {
385  uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable( xRangeObj, uno::UNO_QUERY );
386  if( xCellRangeAddressable.is() )
387  {
388  ScRange aRange;
389  ScUnoConversion::FillScRange( aRange, xCellRangeAddressable->getRangeAddress() );
390  aRangeList.push_back( aRange );
391  }
392  }
393  }
394 
395  if (!aRangeList.empty())
396  {
397  uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( mpDocShell, aRangeList ) );
398  uno::Sequence< uno::Any > aArgs{ uno::Any(xRanges) };
399  mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
400  }
401 }
402 
403 void SAL_CALL ScVbaEventListener::disposing( const lang::EventObject& rEvent )
404 {
405  ::osl::MutexGuard aGuard( maMutex );
406 
407  uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
408  if( xModel.is() )
409  {
410  OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
412  mbDisposed = true;
413  return;
414  }
415 
416  uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
417  if( xController.is() )
418  {
419  stopControllerListening( xController );
420  return;
421  }
422 }
423 
424 // private --------------------------------------------------------------------
425 
427 {
428  try
429  {
430  uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
431  xChangesNotifier->addChangesListener( this );
432  }
433  catch( uno::Exception& )
434  {
435  }
436 }
437 
439 {
440  try
441  {
442  uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
443  xChangesNotifier->removeChangesListener( this );
444  }
445  catch( uno::Exception& )
446  {
447  }
448 }
449 
450 uno::Reference< frame::XController > ScVbaEventListener::getControllerForWindow( vcl::Window* pWindow ) const
451 {
452  WindowControllerMap::const_iterator aIt = maControllers.find( pWindow );
453  return (aIt == maControllers.end()) ? uno::Reference< frame::XController >() : aIt->second;
454 }
455 
457 {
458  uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
459  if( xController.is() )
460  {
461  uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
462  mrVbaEvents.processVbaEventNoThrow( bActivate ? WORKBOOK_WINDOWACTIVATE : WORKBOOK_WINDOWDEACTIVATE, aArgs );
463  }
464 }
465 
467 {
468  // check that the passed window is still alive (it must be registered in maControllers)
469  if( pWindow && (maControllers.count( pWindow ) > 0) )
470  {
472  acquire(); // ensure we don't get deleted before the timer fires
473  m_PostedWindows.insert(pWindow);
474  Application::PostUserEvent( LINK( this, ScVbaEventListener, processWindowResizeEvent ), pWindow );
475  }
476 }
477 
478 IMPL_LINK( ScVbaEventListener, processWindowResizeEvent, void*, p, void )
479 {
480  vcl::Window* pWindow = static_cast<vcl::Window*>(p);
481  ::osl::MutexGuard aGuard( maMutex );
482 
483  /* Check that the passed window is still alive (it must be registered in
484  maControllers). While closing a document, postWindowResizeEvent() may
485  be called on the last window which posts a user event via
486  Application::PostUserEvent to call this event handler. VCL will trigger
487  the handler some time later. Sometimes, the window gets deleted before.
488  This is handled via the disposing() function which removes the window
489  pointer from the member maControllers. Thus, checking whether
490  maControllers contains pWindow ensures that the window is still alive. */
491  if( !mbDisposed && pWindow && !pWindow->isDisposed() && (maControllers.count(pWindow) > 0) )
492  {
493  // do not fire event unless all mouse buttons have been released
494  vcl::Window::PointerState aPointerState = pWindow->GetPointerState();
495  if( (aPointerState.mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0 )
496  {
497  uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
498  if( xController.is() )
499  {
500  uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
501  // #163419# do not throw exceptions into application core
502  mrVbaEvents.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE, aArgs );
503  }
504  }
505  }
506  {
507  // note: there may be multiple processWindowResizeEvent outstanding
508  // for pWindow, so it may have been added to m_PostedWindows multiple
509  // times - so this must delete exactly one of these elements!
510  auto const iter(m_PostedWindows.find(pWindow));
511  assert(iter != m_PostedWindows.end());
512  m_PostedWindows.erase(iter);
513  }
514  release();
515 }
516 
517 ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs ) :
518  VbaEventsHelperBase( rArgs ),
519  mbOpened( false )
520 {
521  mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
522  mpDoc = mpDocShell ? &mpDocShell->GetDocument() : nullptr;
523 
524  if( !mxModel.is() || !mpDocShell || !mpDoc )
525  return;
526 
527  // global
528  auto registerAutoEvent = [this](sal_Int32 nID, const char* sName)
529  { registerEventHandler(nID, script::ModuleType::NORMAL, OString(OString::Concat("Auto_") + sName).getStr(), -1, uno::Any(false)); };
530  registerAutoEvent(AUTO_OPEN, "Open");
531  registerAutoEvent(AUTO_CLOSE, "Close");
532 
533  // Workbook
534  auto registerWorkbookEvent = [this](sal_Int32 nID, const char* sName, sal_Int32 nCancelIndex)
535  { registerEventHandler(nID, script::ModuleType::DOCUMENT, OString(OString::Concat("Workbook_") + sName).getStr(), nCancelIndex, uno::Any(false)); };
536  registerWorkbookEvent( WORKBOOK_ACTIVATE, "Activate", -1 );
537  registerWorkbookEvent( WORKBOOK_DEACTIVATE, "Deactivate", -1 );
538  registerWorkbookEvent( WORKBOOK_OPEN, "Open", -1 );
539  registerWorkbookEvent( WORKBOOK_BEFORECLOSE, "BeforeClose", 0 );
540  registerWorkbookEvent( WORKBOOK_BEFOREPRINT, "BeforePrint", 0 );
541  registerWorkbookEvent( WORKBOOK_BEFORESAVE, "BeforeSave", 1 );
542  registerWorkbookEvent( WORKBOOK_AFTERSAVE, "AfterSave", -1 );
543  registerWorkbookEvent( WORKBOOK_NEWSHEET, "NewSheet", -1 );
544  registerWorkbookEvent( WORKBOOK_WINDOWACTIVATE, "WindowActivate", -1 );
545  registerWorkbookEvent( WORKBOOK_WINDOWDEACTIVATE, "WindowDeactivate", -1 );
546  registerWorkbookEvent( WORKBOOK_WINDOWRESIZE, "WindowResize", -1 );
547 
548  // Worksheet events. All events have a corresponding workbook event.
549  auto registerWorksheetEvent = [this](sal_Int32 nID, const char* sName, sal_Int32 nCancelIndex)
550  {
551  registerEventHandler(nID, script::ModuleType::DOCUMENT, OString(OString::Concat("Worksheet_") + sName).getStr(),
552  nCancelIndex, uno::Any(true));
553  registerEventHandler(USERDEFINED_START + nID, script::ModuleType::DOCUMENT,
554  OString(OString::Concat("Workbook_Worksheet") + sName).getStr(),
555  ((nCancelIndex >= 0) ? (nCancelIndex + 1) : -1), uno::Any(false));
556  };
557  registerWorksheetEvent( WORKSHEET_ACTIVATE, "Activate", -1 );
558  registerWorksheetEvent( WORKSHEET_DEACTIVATE, "Deactivate", -1 );
559  registerWorksheetEvent( WORKSHEET_BEFOREDOUBLECLICK, "BeforeDoubleClick", 1 );
560  registerWorksheetEvent( WORKSHEET_BEFORERIGHTCLICK, "BeforeRightClick", 1 );
561  registerWorksheetEvent( WORKSHEET_CALCULATE, "Calculate", -1 );
562  registerWorksheetEvent( WORKSHEET_CHANGE, "Change", -1 );
563  registerWorksheetEvent( WORKSHEET_SELECTIONCHANGE, "SelectionChange", -1 );
564  registerWorksheetEvent( WORKSHEET_FOLLOWHYPERLINK, "FollowHyperlink", -1 );
565 }
566 
568 {
569 }
570 
571 void SAL_CALL ScVbaEventsHelper::notifyEvent( const css::document::EventObject& rEvent )
572 {
573  static const uno::Sequence< uno::Any > saEmptyArgs;
574  if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC )) ||
575  (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CREATEDOC )) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
576  {
577  processVbaEventNoThrow( WORKBOOK_OPEN, saEmptyArgs );
578  }
579  else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC ) )
580  {
581  processVbaEventNoThrow( WORKBOOK_ACTIVATE, saEmptyArgs );
582  }
583  else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC ) )
584  {
585  processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
586  }
587  else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCDONE )) ||
588  (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCDONE )) ||
589  (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCDONE )) )
590  {
591  uno::Sequence< uno::Any > aArgs{ uno::Any(true) };
592  processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
593  }
594  else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCFAILED )) ||
595  (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCFAILED )) ||
596  (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCFAILED )) )
597  {
598  uno::Sequence< uno::Any > aArgs{ uno::Any(false) };
599  processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
600  }
601  else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ) )
602  {
603  /* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
604  events and stop listening to the model (done in base class). */
605  uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
606  if( xController.is() )
607  {
608  uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
609  processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE, aArgs );
610  }
611  processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
612  }
613  else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED ) )
614  {
615  uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
616  if( mxListener && xController.is() )
617  mxListener->startControllerListening( xController );
618  }
620 }
621 
623 {
624  return "ScVbaEventsHelper";
625 }
626 
627 css::uno::Sequence<OUString> ScVbaEventsHelper::getSupportedServiceNames()
628 {
629  return css::uno::Sequence<OUString>{
630  "com.sun.star.script.vba.VBASpreadsheetEventProcessor"};
631 }
632 
633 // protected ------------------------------------------------------------------
634 
636  const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& rArgs )
637 {
638  // document and document shell are needed during event processing
639  if( !mpShell || !mpDoc )
640  throw uno::RuntimeException();
641 
642  /* For document events: check if events are enabled via the
643  Application.EnableEvents symbol (this is an Excel-only attribute).
644  Check this again for every event, as the event handler may change the
645  state of the EnableEvents symbol. Global events such as AUTO_OPEN and
646  AUTO_CLOSE are always enabled. */
647  bool bExecuteEvent = (rInfo.mnModuleType != script::ModuleType::DOCUMENT) || ScVbaApplication::getDocumentEventsEnabled();
648 
649  // framework and Calc fire a few events before 'OnLoad', ignore them
650  if( bExecuteEvent )
651  bExecuteEvent = (rInfo.mnEventId == WORKBOOK_OPEN) ? !mbOpened : mbOpened;
652 
653  // special handling for some events
654  if( bExecuteEvent ) switch( rInfo.mnEventId )
655  {
656  case WORKBOOK_OPEN:
657  {
658  // execute delayed Activate event too (see above)
659  rEventQueue.emplace_back(WORKBOOK_ACTIVATE );
660  uno::Sequence< uno::Any > aArgs{ uno::Any(mxModel->getCurrentController()) };
661  rEventQueue.emplace_back( WORKBOOK_WINDOWACTIVATE, aArgs );
662  rEventQueue.emplace_back(AUTO_OPEN );
663  // remember initial selection
664  maOldSelection <<= mxModel->getCurrentSelection();
665  }
666  break;
667  case WORKSHEET_SELECTIONCHANGE:
668  // if selection is not changed, then do not fire the event
669  bExecuteEvent = isSelectionChanged( rArgs, 0 );
670  break;
671  }
672 
673  if( bExecuteEvent )
674  {
675  // add workbook event associated to a sheet event
676  bool bSheetEvent = false;
677  if( (rInfo.maUserData >>= bSheetEvent) && bSheetEvent )
678  rEventQueue.emplace_back( rInfo.mnEventId + USERDEFINED_START, rArgs );
679  }
680 
681  return bExecuteEvent;
682 }
683 
684 uno::Sequence< uno::Any > ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& rInfo,
685  const uno::Sequence< uno::Any >& rArgs )
686 {
687  // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
688  bool bSheetEventAsBookEvent = rInfo.mnEventId > USERDEFINED_START;
689  sal_Int32 nEventId = bSheetEventAsBookEvent ? (rInfo.mnEventId - USERDEFINED_START) : rInfo.mnEventId;
690 
691  uno::Sequence< uno::Any > aVbaArgs;
692  switch( nEventId )
693  {
694  // *** Workbook ***
695 
696  // no arguments
697  case WORKBOOK_ACTIVATE:
698  case WORKBOOK_DEACTIVATE:
699  case WORKBOOK_OPEN:
700  break;
701  // 1 arg: cancel
702  case WORKBOOK_BEFORECLOSE:
703  case WORKBOOK_BEFOREPRINT:
704  aVbaArgs.realloc( 1 );
705  // current cancel state will be inserted by caller
706  break;
707  // 2 args: saveAs, cancel
708  case WORKBOOK_BEFORESAVE:
709  checkArgumentType< bool >( rArgs, 0 );
710  aVbaArgs = { rArgs[ 0 ], {} };
711  // current cancel state will be inserted by caller
712  break;
713  // 1 arg: success
714  case WORKBOOK_AFTERSAVE:
715  checkArgumentType< bool >( rArgs, 0 );
716  aVbaArgs = { rArgs[ 0 ] };
717  break;
718  // 1 arg: window
719  case WORKBOOK_WINDOWACTIVATE:
720  case WORKBOOK_WINDOWDEACTIVATE:
721  case WORKBOOK_WINDOWRESIZE:
722  aVbaArgs = { createWindow( rArgs, 0 ) };
723  break;
724  // 1 arg: worksheet
725  case WORKBOOK_NEWSHEET:
726  aVbaArgs = { createWorksheet( rArgs, 0 ) };
727  break;
728 
729  // *** Worksheet ***
730 
731  // no arguments
732  case WORKSHEET_ACTIVATE:
733  case WORKSHEET_CALCULATE:
734  case WORKSHEET_DEACTIVATE:
735  break;
736  // 1 arg: range
737  case WORKSHEET_CHANGE:
738  case WORKSHEET_SELECTIONCHANGE:
739  aVbaArgs = { createRange( rArgs, 0 ) };
740  break;
741  // 2 args: range, cancel
742  case WORKSHEET_BEFOREDOUBLECLICK:
743  case WORKSHEET_BEFORERIGHTCLICK:
744  aVbaArgs = { createRange( rArgs, 0 ), {} };
745  // current cancel state will be inserted by caller
746  break;
747  // 1 arg: hyperlink
748  case WORKSHEET_FOLLOWHYPERLINK:
749  aVbaArgs = { createHyperlink( rArgs, 0 ) };
750  break;
751  }
752 
753  /* For workbook events associated to sheet events, the workbook event gets
754  the same arguments but with a Worksheet object in front of them. */
755  if( bSheetEventAsBookEvent )
756  {
757  sal_Int32 nLength = aVbaArgs.getLength();
758  uno::Sequence< uno::Any > aVbaArgs2( nLength + 1 );
759  auto pVbaArgs2 = aVbaArgs2.getArray();
760  *pVbaArgs2 = createWorksheet( rArgs, 0 );
761  std::copy_n(std::cbegin(aVbaArgs), nLength, std::next(pVbaArgs2));
762  aVbaArgs = aVbaArgs2;
763  }
764 
765  return aVbaArgs;
766 }
767 
769  const EventHandlerInfo& rInfo, bool bCancel )
770 {
771  switch( rInfo.mnEventId )
772  {
773  case WORKBOOK_OPEN:
774  mbOpened = true;
775  // register the listeners
776  if( !mxListener.is() )
778  break;
779  case WORKBOOK_BEFORECLOSE:
780  /* Execute Auto_Close only if not cancelled by event handler, but
781  before UI asks user whether to cancel closing the document. */
782  if( !bCancel )
783  rEventQueue.emplace_back(AUTO_CLOSE );
784  break;
785  }
786 }
787 
788 OUString ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& rInfo,
789  const uno::Sequence< uno::Any >& rArgs ) const
790 {
791  bool bSheetEvent = false;
792  rInfo.maUserData >>= bSheetEvent;
793  SCTAB nTab = bSheetEvent ? lclGetTabFromArgs( rArgs, 0 ) : -1;
794  if( bSheetEvent && (nTab < 0) )
795  throw lang::IllegalArgumentException();
796 
797  OUString aCodeName;
798  if( bSheetEvent )
799  mpDoc->GetCodeName( nTab, aCodeName );
800  else
801  aCodeName = mpDoc->GetCodeName();
802  return aCodeName;
803 }
804 
805 // private --------------------------------------------------------------------
806 
807 namespace {
808 
811 bool lclSelectionChanged( const ScRangeList& rLeft, const ScRangeList& rRight )
812 {
813  // one of the range lists empty? -> return false, if both lists empty
814  bool bLeftEmpty = rLeft.empty();
815  bool bRightEmpty = rRight.empty();
816  if( bLeftEmpty || bRightEmpty )
817  return !(bLeftEmpty && bRightEmpty);
818 
819  // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
820  if (rLeft[0].aStart.Tab() != rRight[0].aStart.Tab())
821  return false;
822 
823  // compare all ranges
824  return rLeft != rRight;
825 }
826 
827 } // namespace
828 
829 bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex )
830 {
831  uno::Reference< uno::XInterface > xOldSelection( maOldSelection, uno::UNO_QUERY );
832  uno::Reference< uno::XInterface > xNewSelection = getXSomethingFromArgs< uno::XInterface >( rArgs, nIndex, false );
833  ScCellRangesBase* pOldCellRanges = comphelper::getFromUnoTunnel<ScCellRangesBase>( xOldSelection );
834  ScCellRangesBase* pNewCellRanges = comphelper::getFromUnoTunnel<ScCellRangesBase>( xNewSelection );
835  bool bChanged = !pOldCellRanges || !pNewCellRanges || lclSelectionChanged( pOldCellRanges->GetRangeList(), pNewCellRanges->GetRangeList() );
836  maOldSelection <<= xNewSelection;
837  return bChanged;
838 }
839 
840 uno::Any ScVbaEventsHelper::createWorksheet( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
841 {
842  // extract sheet index, will throw, if parameter is invalid
843  SCTAB nTab = lclGetTabFromArgs( rArgs, nIndex );
844  return uno::Any( excel::getUnoSheetModuleObj( mxModel, nTab ) );
845 }
846 
847 uno::Any ScVbaEventsHelper::createRange( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
848 {
849  // it is possible to pass an existing VBA Range object
850  uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
851  if( !xVbaRange.is() )
852  {
853  uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
854  uno::Reference< table::XCellRange > xRange = getXSomethingFromArgs< table::XCellRange >( rArgs, nIndex );
855  if ( !xRanges.is() && !xRange.is() )
856  throw lang::IllegalArgumentException();
857 
858  uno::Sequence< uno::Any > aArgs;
859  if ( xRanges.is() )
860  {
861  aArgs = { uno::Any(excel::getUnoSheetModuleObj( xRanges )), uno::Any(xRanges) };
862  }
863  else
864  {
865  aArgs = { uno::Any(excel::getUnoSheetModuleObj( xRange )), uno::Any(xRange) };
866  }
867  xVbaRange.set( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Range", aArgs ), uno::UNO_QUERY_THROW );
868  }
869  return uno::Any( xVbaRange );
870 }
871 
872 uno::Any ScVbaEventsHelper::createHyperlink( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
873 {
874  uno::Reference< table::XCell > xCell = getXSomethingFromArgs< table::XCell >( rArgs, nIndex, false );
875  uno::Sequence< uno::Any > aArgs{ uno::Any(excel::getUnoSheetModuleObj( xCell )),
876  uno::Any(xCell) };
877  uno::Reference< uno::XInterface > xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Hyperlink", aArgs ), uno::UNO_SET_THROW );
878  return uno::Any( xHyperlink );
879 }
880 
881 uno::Any ScVbaEventsHelper::createWindow( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
882 {
883  uno::Sequence< uno::Any > aArgs{ uno::Any(getVBADocument( mxModel )),
884  uno::Any(mxModel),
885  uno::Any(getXSomethingFromArgs< frame::XController >( rArgs, nIndex, false )) };
886  uno::Reference< uno::XInterface > xWindow( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Window", aArgs ), uno::UNO_SET_THROW );
887  return uno::Any( xWindow );
888 }
889 
890 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
892  css::uno::XComponentContext * /*context*/,
893  css::uno::Sequence<css::uno::Any> const &arguments)
894 {
895  return cppu::acquire(new ScVbaEventsHelper(arguments));
896 }
897 
898 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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
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::mutex maMutex
::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)
static vcl::Window * GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
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:1137
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
bool empty() const
Definition: rangelst.hxx:88
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:70
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:220
virtual void SAL_CALL windowHidden(const lang::EventObject &rEvent) override
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:79
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
bool isDisposed() const
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:22
bool m_bDetectedRangeSegmentation false