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