1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "vbahelper/vbaeventshelperbase.hxx"
25 #include <com/sun/star/document/XEventBroadcaster.hpp>
26 #include <com/sun/star/script/ModuleType.hpp>
27 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
28 #include <com/sun/star/util/XChangesNotifier.hpp>
29 #include <filter/msfilter/msvbahelper.hxx>
30 #include <unotools/eventcfg.hxx>
31 
32 using namespace ::com::sun::star;
33 using namespace ::ooo::vba;
34 
35 using ::rtl::OUString;
36 using ::rtl::OUStringBuffer;
37 
38 // ============================================================================
39 
VbaEventsHelperBase(const uno::Sequence<uno::Any> & rArgs,const uno::Reference<uno::XComponentContext> &)40 VbaEventsHelperBase::VbaEventsHelperBase( const uno::Sequence< uno::Any >& rArgs, const uno::Reference< uno::XComponentContext >& /*xContext*/ ) :
41     mpShell( 0 ),
42     mbDisposed( true )
43 {
44     try
45     {
46         mxModel = getXSomethingFromArgs< frame::XModel >( rArgs, 0, false );
47         mpShell = getSfxObjShell( mxModel );
48     }
49     catch( uno::Exception& )
50     {
51     }
52     mbDisposed = mpShell == 0;
53     startListening();
54 }
55 
~VbaEventsHelperBase()56 VbaEventsHelperBase::~VbaEventsHelperBase()
57 {
58     OSL_ENSURE( mbDisposed, "VbaEventsHelperBase::~VbaEventsHelperBase - missing disposing notification" );
59 }
60 
hasVbaEventHandler(sal_Int32 nEventId,const uno::Sequence<uno::Any> & rArgs)61 sal_Bool SAL_CALL VbaEventsHelperBase::hasVbaEventHandler( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
62         throw (lang::IllegalArgumentException, uno::RuntimeException)
63 {
64     // getEventHandlerInfo() throws, if unknown event dentifier has been passed
65     const EventHandlerInfo& rInfo = getEventHandlerInfo( nEventId );
66     // getEventHandlerPath() searches for the macro in the document
67     return getEventHandlerPath( rInfo, rArgs ).getLength() > 0;
68 }
69 
processVbaEvent(sal_Int32 nEventId,const uno::Sequence<uno::Any> & rArgs)70 sal_Bool SAL_CALL VbaEventsHelperBase::processVbaEvent( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
71         throw (lang::IllegalArgumentException, util::VetoException, uno::RuntimeException)
72 {
73     /*  Derived classes may add new event identifiers to be processed while
74         processing the original event. All unprocessed events are collected in
75         a queue. First element in the queue is the next event to be processed. */
76     EventQueue aEventQueue;
77     aEventQueue.push_back( EventQueueEntry( nEventId, rArgs ) );
78 
79     /*  bCancel will contain the current Cancel value. It is possible that
80         multiple events will try to modify the Cancel value. Every event
81         handler receives the Cancel value of the previous event handler. */
82     bool bCancel = false;
83 
84     /*  bExecuted will change to true if at least one event handler has been
85         found and executed. */
86     bool bExecuted = false;
87 
88     /*  Loop as long as there are more events to be processed. Derived classes
89         may add new events to be processed in the virtual implPrepareEvent()
90         function. */
91     while( !aEventQueue.empty() )
92     {
93         /*  Check that all class members are available, and that we are not
94             disposed (this may have happened at any time during execution of
95             the last event handler). */
96         if( mbDisposed || !mxModel.is() || !mpShell )
97             throw uno::RuntimeException();
98 
99         // get info for next event
100         const EventHandlerInfo& rInfo = getEventHandlerInfo( aEventQueue.front().mnEventId );
101         uno::Sequence< uno::Any > aEventArgs = aEventQueue.front().maArgs;
102         aEventQueue.pop_front();
103         OSL_TRACE( "VbaEventsHelperBase::processVbaEvent( \"%s\" )", ::rtl::OUStringToOString( rInfo.maMacroName, RTL_TEXTENCODING_UTF8 ).getStr() );
104 
105         /*  Let derived classes prepare the event, they may add new events for
106             next iteration. If false is returned, the event handler must not be
107             called. */
108         if( implPrepareEvent( aEventQueue, rInfo, aEventArgs ) )
109         {
110             // search the event handler macro in the document
111             OUString aMacroPath = getEventHandlerPath( rInfo, aEventArgs );
112             if( aMacroPath.getLength() > 0 )
113             {
114                 // build the argument list
115                 uno::Sequence< uno::Any > aVbaArgs = implBuildArgumentList( rInfo, aEventArgs );
116                 // insert current cancel value
117                 if( rInfo.mnCancelIndex >= 0 )
118                 {
119                     if( rInfo.mnCancelIndex >= aVbaArgs.getLength() )
120                         throw lang::IllegalArgumentException();
121                     aVbaArgs[ rInfo.mnCancelIndex ] <<= bCancel;
122                 }
123                 // execute the event handler
124                 uno::Any aRet, aCaller;
125                 executeMacro( mpShell, aMacroPath, aVbaArgs, aRet, aCaller );
126                 // extract new cancel value (may be boolean or any integer type)
127                 if( rInfo.mnCancelIndex >= 0 )
128                 {
129                     checkArgument( aVbaArgs, rInfo.mnCancelIndex );
130                     bCancel = extractBoolFromAny( aVbaArgs[ rInfo.mnCancelIndex ] );
131                 }
132                 // event handler has been found
133                 bExecuted = true;
134             }
135         }
136         // post processing (also, if event handler does not exist, or disabled, or on error
137         implPostProcessEvent( aEventQueue, rInfo, bCancel );
138     }
139 
140     // if event handlers want to cancel the event, do so regardless of any errors
141     if( bCancel )
142         throw util::VetoException();
143 
144     // return true, if at least one event handler has been found
145     return bExecuted;
146 }
147 
notifyEvent(const document::EventObject & rEvent)148 void SAL_CALL VbaEventsHelperBase::notifyEvent( const document::EventObject& rEvent ) throw (uno::RuntimeException)
149 {
150     OSL_TRACE( "VbaEventsHelperBase::notifyEvent( \"%s\" )", ::rtl::OUStringToOString( rEvent.EventName, RTL_TEXTENCODING_UTF8 ).getStr() );
151     if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CLOSEDOC ) )
152         stopListening();
153 }
154 
changesOccurred(const util::ChangesEvent & rEvent)155 void SAL_CALL VbaEventsHelperBase::changesOccurred( const util::ChangesEvent& rEvent ) throw (uno::RuntimeException)
156 {
157     // make sure the VBA library exists
158     try
159     {
160         ensureVBALibrary();
161     }
162     catch( uno::Exception& )
163     {
164         return;
165     }
166 
167     // check that the sender of the event is the VBA library
168     uno::Reference< script::vba::XVBAModuleInfo > xSender( rEvent.Base, uno::UNO_QUERY );
169     if( mxModuleInfos.get() != xSender.get() )
170         return;
171 
172     // process all changed modules
173     for( sal_Int32 nIndex = 0, nLength = rEvent.Changes.getLength(); nIndex < nLength; ++nIndex )
174     {
175         const util::ElementChange& rChange = rEvent.Changes[ nIndex ];
176         OUString aModuleName;
177         if( (rChange.Accessor >>= aModuleName) && (aModuleName.getLength() > 0) ) try
178         {
179             // invalidate event handler path map depending on module type
180             if( getModuleType( aModuleName ) == script::ModuleType::NORMAL )
181                 // paths to global event handlers are stored with empty key (will be searched in all normal code modules)
182                 maEventPaths.erase( OUString() );
183             else
184                 // paths to class/form/document event handlers are keyed by module name
185                 maEventPaths.erase( aModuleName );
186         }
187         catch( uno::Exception& )
188         {
189         }
190     }
191 }
192 
disposing(const lang::EventObject & rEvent)193 void SAL_CALL VbaEventsHelperBase::disposing( const lang::EventObject& rEvent ) throw (uno::RuntimeException)
194 {
195     uno::Reference< frame::XModel > xSender( rEvent.Source, uno::UNO_QUERY );
196     if( xSender.is() )
197         stopListening();
198 }
199 
processVbaEventNoThrow(sal_Int32 nEventId,const uno::Sequence<uno::Any> & rArgs)200 void VbaEventsHelperBase::processVbaEventNoThrow( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
201 {
202     try
203     {
204         processVbaEvent( nEventId, rArgs );
205     }
206     catch( uno::Exception& )
207     {
208     }
209 }
210 
211 // protected ------------------------------------------------------------------
212 
registerEventHandler(sal_Int32 nEventId,sal_Int32 nModuleType,const sal_Char * pcMacroName,sal_Int32 nCancelIndex,const uno::Any & rUserData)213 void VbaEventsHelperBase::registerEventHandler( sal_Int32 nEventId, sal_Int32 nModuleType,
214         const sal_Char* pcMacroName, sal_Int32 nCancelIndex, const uno::Any& rUserData )
215 {
216     EventHandlerInfo& rInfo = maEventInfos[ nEventId ];
217     rInfo.mnEventId = nEventId;
218     rInfo.mnModuleType = nModuleType;
219     rInfo.maMacroName = OUString::createFromAscii( pcMacroName );
220     rInfo.mnCancelIndex = nCancelIndex;
221     rInfo.maUserData = rUserData;
222 }
223 
224 // private --------------------------------------------------------------------
225 
startListening()226 void VbaEventsHelperBase::startListening()
227 {
228     if( mbDisposed )
229         return;
230 
231     uno::Reference< document::XEventBroadcaster > xEventBroadcaster( mxModel, uno::UNO_QUERY );
232     if( xEventBroadcaster.is() )
233         try { xEventBroadcaster->addEventListener( this ); } catch( uno::Exception& ) {}
234 }
235 
stopListening()236 void VbaEventsHelperBase::stopListening()
237 {
238     if( mbDisposed )
239         return;
240 
241     uno::Reference< document::XEventBroadcaster > xEventBroadcaster( mxModel, uno::UNO_QUERY );
242     if( xEventBroadcaster.is() )
243         try { xEventBroadcaster->removeEventListener( this ); } catch( uno::Exception& ) {}
244 
245     mxModel.clear();
246     mpShell = 0;
247     maEventInfos.clear();
248     mbDisposed = true;
249 }
250 
getEventHandlerInfo(sal_Int32 nEventId) const251 const VbaEventsHelperBase::EventHandlerInfo& VbaEventsHelperBase::getEventHandlerInfo(
252         sal_Int32 nEventId ) const throw (lang::IllegalArgumentException)
253 {
254     EventHandlerInfoMap::const_iterator aIt = maEventInfos.find( nEventId );
255     if( aIt == maEventInfos.end() )
256         throw lang::IllegalArgumentException();
257     return aIt->second;
258 }
259 
getEventHandlerPath(const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs)260 OUString VbaEventsHelperBase::getEventHandlerPath( const EventHandlerInfo& rInfo,
261         const uno::Sequence< uno::Any >& rArgs ) throw (lang::IllegalArgumentException, uno::RuntimeException)
262 {
263     OUString aModuleName;
264     switch( rInfo.mnModuleType )
265     {
266         // global event handlers may exist in any standard code module
267         case script::ModuleType::NORMAL:
268         break;
269 
270         // document event: get name of the code module associated to the event sender
271         case script::ModuleType::DOCUMENT:
272             aModuleName = implGetDocumentModuleName( rInfo, rArgs );
273             if( aModuleName.getLength() == 0 )
274                 throw lang::IllegalArgumentException();
275         break;
276 
277         default:
278             throw uno::RuntimeException(); // unsupported module type
279     }
280 
281     /*  Performance improvement: Check the list of existing event handlers
282         instead of searching in Basic source code every time. */
283     EventHandlerPathMap::iterator aIt = maEventPaths.find( aModuleName );
284     ModulePathMap& rPathMap = (aIt == maEventPaths.end()) ? updateModulePathMap( aModuleName ) : aIt->second;
285     return rPathMap[ rInfo.mnEventId ];
286 }
287 
ensureVBALibrary()288 void VbaEventsHelperBase::ensureVBALibrary() throw (uno::RuntimeException)
289 {
290     if( !mxModuleInfos.is() ) try
291     {
292         maLibraryName = getDefaultProjectName( mpShell );
293         if( maLibraryName.getLength() == 0 )
294             throw uno::RuntimeException();
295         uno::Reference< beans::XPropertySet > xModelProps( mxModel, uno::UNO_QUERY_THROW );
296         uno::Reference< container::XNameAccess > xBasicLibs( xModelProps->getPropertyValue(
297             OUString( RTL_CONSTASCII_USTRINGPARAM( "BasicLibraries" ) ) ), uno::UNO_QUERY_THROW );
298         mxModuleInfos.set( xBasicLibs->getByName( maLibraryName ), uno::UNO_QUERY_THROW );
299         // listen to changes in the VBA source code
300         uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModuleInfos, uno::UNO_QUERY_THROW );
301         xChangesNotifier->addChangesListener( this );
302     }
303     catch( uno::Exception& )
304     {
305         // error accessing the Basic library, so this object is useless
306         stopListening();
307         throw uno::RuntimeException();
308     }
309 }
310 
getModuleType(const OUString & rModuleName)311 sal_Int32 VbaEventsHelperBase::getModuleType( const OUString& rModuleName ) throw (uno::RuntimeException)
312 {
313     // make sure the VBA library exists
314     ensureVBALibrary();
315 
316     // no module specified: global event handler in standard code modules
317     if( rModuleName.getLength() == 0 )
318         return script::ModuleType::NORMAL;
319 
320     // get module type from module info
321     try
322     {
323         return mxModuleInfos->getModuleInfo( rModuleName ).ModuleType;
324     }
325     catch( uno::Exception& )
326     {
327     }
328     throw uno::RuntimeException();
329 }
330 
updateModulePathMap(const::rtl::OUString & rModuleName)331 VbaEventsHelperBase::ModulePathMap& VbaEventsHelperBase::updateModulePathMap( const ::rtl::OUString& rModuleName ) throw (uno::RuntimeException)
332 {
333     // get type of the specified module (throws on error)
334     sal_Int32 nModuleType = getModuleType( rModuleName );
335     // search for all event handlers
336     ModulePathMap& rPathMap = maEventPaths[ rModuleName ];
337     for( EventHandlerInfoMap::iterator aIt = maEventInfos.begin(), aEnd = maEventInfos.end(); aIt != aEnd; ++aIt )
338     {
339         const EventHandlerInfo& rInfo = aIt->second;
340         if( rInfo.mnModuleType == nModuleType )
341             rPathMap[ rInfo.mnEventId ] = resolveVBAMacro( mpShell, maLibraryName, rModuleName, rInfo.maMacroName );
342     }
343     return rPathMap;
344 }
345 
346 // ============================================================================
347