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