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 {
63 // getEventHandlerInfo() throws, if unknown event dentifier has been passed
64 const EventHandlerInfo& rInfo = getEventHandlerInfo( nEventId );
65 // getEventHandlerPath() searches for the macro in the document
66 return getEventHandlerPath( rInfo, rArgs ).getLength() > 0;
67 }
68
processVbaEvent(sal_Int32 nEventId,const uno::Sequence<uno::Any> & rArgs)69 sal_Bool SAL_CALL VbaEventsHelperBase::processVbaEvent( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
70 {
71 /* Derived classes may add new event identifiers to be processed while
72 processing the original event. All unprocessed events are collected in
73 a queue. First element in the queue is the next event to be processed. */
74 EventQueue aEventQueue;
75 aEventQueue.push_back( EventQueueEntry( nEventId, rArgs ) );
76
77 /* bCancel will contain the current Cancel value. It is possible that
78 multiple events will try to modify the Cancel value. Every event
79 handler receives the Cancel value of the previous event handler. */
80 bool bCancel = false;
81
82 /* bExecuted will change to true if at least one event handler has been
83 found and executed. */
84 bool bExecuted = false;
85
86 /* Loop as long as there are more events to be processed. Derived classes
87 may add new events to be processed in the virtual implPrepareEvent()
88 function. */
89 while( !aEventQueue.empty() )
90 {
91 /* Check that all class members are available, and that we are not
92 disposed (this may have happened at any time during execution of
93 the last event handler). */
94 if( mbDisposed || !mxModel.is() || !mpShell )
95 throw uno::RuntimeException();
96
97 // get info for next event
98 const EventHandlerInfo& rInfo = getEventHandlerInfo( aEventQueue.front().mnEventId );
99 uno::Sequence< uno::Any > aEventArgs = aEventQueue.front().maArgs;
100 aEventQueue.pop_front();
101 OSL_TRACE( "VbaEventsHelperBase::processVbaEvent( \"%s\" )", ::rtl::OUStringToOString( rInfo.maMacroName, RTL_TEXTENCODING_UTF8 ).getStr() );
102
103 /* Let derived classes prepare the event, they may add new events for
104 next iteration. If false is returned, the event handler must not be
105 called. */
106 if( implPrepareEvent( aEventQueue, rInfo, aEventArgs ) )
107 {
108 // search the event handler macro in the document
109 OUString aMacroPath = getEventHandlerPath( rInfo, aEventArgs );
110 if( aMacroPath.getLength() > 0 )
111 {
112 // build the argument list
113 uno::Sequence< uno::Any > aVbaArgs = implBuildArgumentList( rInfo, aEventArgs );
114 // insert current cancel value
115 if( rInfo.mnCancelIndex >= 0 )
116 {
117 if( rInfo.mnCancelIndex >= aVbaArgs.getLength() )
118 throw lang::IllegalArgumentException();
119 aVbaArgs[ rInfo.mnCancelIndex ] <<= bCancel;
120 }
121 // execute the event handler
122 uno::Any aRet, aCaller;
123 executeMacro( mpShell, aMacroPath, aVbaArgs, aRet, aCaller );
124 // extract new cancel value (may be boolean or any integer type)
125 if( rInfo.mnCancelIndex >= 0 )
126 {
127 checkArgument( aVbaArgs, rInfo.mnCancelIndex );
128 bCancel = extractBoolFromAny( aVbaArgs[ rInfo.mnCancelIndex ] );
129 }
130 // event handler has been found
131 bExecuted = true;
132 }
133 }
134 // post processing (also, if event handler does not exist, or disabled, or on error
135 implPostProcessEvent( aEventQueue, rInfo, bCancel );
136 }
137
138 // if event handlers want to cancel the event, do so regardless of any errors
139 if( bCancel )
140 throw util::VetoException();
141
142 // return true, if at least one event handler has been found
143 return bExecuted;
144 }
145
notifyEvent(const document::EventObject & rEvent)146 void SAL_CALL VbaEventsHelperBase::notifyEvent( const document::EventObject& rEvent )
147 {
148 OSL_TRACE( "VbaEventsHelperBase::notifyEvent( \"%s\" )", ::rtl::OUStringToOString( rEvent.EventName, RTL_TEXTENCODING_UTF8 ).getStr() );
149 if( rEvent.EventName == GlobalEventConfig::GetEventName( STR_EVENT_CLOSEDOC ) )
150 stopListening();
151 }
152
changesOccurred(const util::ChangesEvent & rEvent)153 void SAL_CALL VbaEventsHelperBase::changesOccurred( const util::ChangesEvent& rEvent )
154 {
155 // make sure the VBA library exists
156 try
157 {
158 ensureVBALibrary();
159 }
160 catch( uno::Exception& )
161 {
162 return;
163 }
164
165 // check that the sender of the event is the VBA library
166 uno::Reference< script::vba::XVBAModuleInfo > xSender( rEvent.Base, uno::UNO_QUERY );
167 if( mxModuleInfos.get() != xSender.get() )
168 return;
169
170 // process all changed modules
171 for( sal_Int32 nIndex = 0, nLength = rEvent.Changes.getLength(); nIndex < nLength; ++nIndex )
172 {
173 const util::ElementChange& rChange = rEvent.Changes[ nIndex ];
174 OUString aModuleName;
175 if( (rChange.Accessor >>= aModuleName) && (aModuleName.getLength() > 0) ) try
176 {
177 // invalidate event handler path map depending on module type
178 if( getModuleType( aModuleName ) == script::ModuleType::NORMAL )
179 // paths to global event handlers are stored with empty key (will be searched in all normal code modules)
180 maEventPaths.erase( OUString() );
181 else
182 // paths to class/form/document event handlers are keyed by module name
183 maEventPaths.erase( aModuleName );
184 }
185 catch( uno::Exception& )
186 {
187 }
188 }
189 }
190
disposing(const lang::EventObject & rEvent)191 void SAL_CALL VbaEventsHelperBase::disposing( const lang::EventObject& rEvent )
192 {
193 uno::Reference< frame::XModel > xSender( rEvent.Source, uno::UNO_QUERY );
194 if( xSender.is() )
195 stopListening();
196 }
197
processVbaEventNoThrow(sal_Int32 nEventId,const uno::Sequence<uno::Any> & rArgs)198 void VbaEventsHelperBase::processVbaEventNoThrow( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
199 {
200 try
201 {
202 processVbaEvent( nEventId, rArgs );
203 }
204 catch( uno::Exception& )
205 {
206 }
207 }
208
209 // protected ------------------------------------------------------------------
210
registerEventHandler(sal_Int32 nEventId,sal_Int32 nModuleType,const sal_Char * pcMacroName,sal_Int32 nCancelIndex,const uno::Any & rUserData)211 void VbaEventsHelperBase::registerEventHandler( sal_Int32 nEventId, sal_Int32 nModuleType,
212 const sal_Char* pcMacroName, sal_Int32 nCancelIndex, const uno::Any& rUserData )
213 {
214 EventHandlerInfo& rInfo = maEventInfos[ nEventId ];
215 rInfo.mnEventId = nEventId;
216 rInfo.mnModuleType = nModuleType;
217 rInfo.maMacroName = OUString::createFromAscii( pcMacroName );
218 rInfo.mnCancelIndex = nCancelIndex;
219 rInfo.maUserData = rUserData;
220 }
221
222 // private --------------------------------------------------------------------
223
startListening()224 void VbaEventsHelperBase::startListening()
225 {
226 if( mbDisposed )
227 return;
228
229 uno::Reference< document::XEventBroadcaster > xEventBroadcaster( mxModel, uno::UNO_QUERY );
230 if( xEventBroadcaster.is() )
231 try { xEventBroadcaster->addEventListener( this ); } catch( uno::Exception& ) {}
232 }
233
stopListening()234 void VbaEventsHelperBase::stopListening()
235 {
236 if( mbDisposed )
237 return;
238
239 uno::Reference< document::XEventBroadcaster > xEventBroadcaster( mxModel, uno::UNO_QUERY );
240 if( xEventBroadcaster.is() )
241 try { xEventBroadcaster->removeEventListener( this ); } catch( uno::Exception& ) {}
242
243 mxModel.clear();
244 mpShell = 0;
245 maEventInfos.clear();
246 mbDisposed = true;
247 }
248
getEventHandlerInfo(sal_Int32 nEventId) const249 const VbaEventsHelperBase::EventHandlerInfo& VbaEventsHelperBase::getEventHandlerInfo(
250 sal_Int32 nEventId ) const
251 {
252 EventHandlerInfoMap::const_iterator aIt = maEventInfos.find( nEventId );
253 if( aIt == maEventInfos.end() )
254 throw lang::IllegalArgumentException();
255 return aIt->second;
256 }
257
getEventHandlerPath(const EventHandlerInfo & rInfo,const uno::Sequence<uno::Any> & rArgs)258 OUString VbaEventsHelperBase::getEventHandlerPath( const EventHandlerInfo& rInfo,
259 const uno::Sequence< uno::Any >& rArgs )
260 {
261 OUString aModuleName;
262 switch( rInfo.mnModuleType )
263 {
264 // global event handlers may exist in any standard code module
265 case script::ModuleType::NORMAL:
266 break;
267
268 // document event: get name of the code module associated to the event sender
269 case script::ModuleType::DOCUMENT:
270 aModuleName = implGetDocumentModuleName( rInfo, rArgs );
271 if( aModuleName.getLength() == 0 )
272 throw lang::IllegalArgumentException();
273 break;
274
275 default:
276 throw uno::RuntimeException(); // unsupported module type
277 }
278
279 /* Performance improvement: Check the list of existing event handlers
280 instead of searching in Basic source code every time. */
281 EventHandlerPathMap::iterator aIt = maEventPaths.find( aModuleName );
282 ModulePathMap& rPathMap = (aIt == maEventPaths.end()) ? updateModulePathMap( aModuleName ) : aIt->second;
283 return rPathMap[ rInfo.mnEventId ];
284 }
285
ensureVBALibrary()286 void VbaEventsHelperBase::ensureVBALibrary()
287 {
288 if( !mxModuleInfos.is() ) try
289 {
290 maLibraryName = getDefaultProjectName( mpShell );
291 if( maLibraryName.getLength() == 0 )
292 throw uno::RuntimeException();
293 uno::Reference< beans::XPropertySet > xModelProps( mxModel, uno::UNO_QUERY_THROW );
294 uno::Reference< container::XNameAccess > xBasicLibs( xModelProps->getPropertyValue(
295 OUString( RTL_CONSTASCII_USTRINGPARAM( "BasicLibraries" ) ) ), uno::UNO_QUERY_THROW );
296 mxModuleInfos.set( xBasicLibs->getByName( maLibraryName ), uno::UNO_QUERY_THROW );
297 // listen to changes in the VBA source code
298 uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModuleInfos, uno::UNO_QUERY_THROW );
299 xChangesNotifier->addChangesListener( this );
300 }
301 catch( uno::Exception& )
302 {
303 // error accessing the Basic library, so this object is useless
304 stopListening();
305 throw uno::RuntimeException();
306 }
307 }
308
getModuleType(const OUString & rModuleName)309 sal_Int32 VbaEventsHelperBase::getModuleType( const OUString& rModuleName )
310 {
311 // make sure the VBA library exists
312 ensureVBALibrary();
313
314 // no module specified: global event handler in standard code modules
315 if( rModuleName.getLength() == 0 )
316 return script::ModuleType::NORMAL;
317
318 // get module type from module info
319 try
320 {
321 return mxModuleInfos->getModuleInfo( rModuleName ).ModuleType;
322 }
323 catch( uno::Exception& )
324 {
325 }
326 throw uno::RuntimeException();
327 }
328
updateModulePathMap(const::rtl::OUString & rModuleName)329 VbaEventsHelperBase::ModulePathMap& VbaEventsHelperBase::updateModulePathMap( const ::rtl::OUString& rModuleName )
330 {
331 // get type of the specified module (throws on error)
332 sal_Int32 nModuleType = getModuleType( rModuleName );
333 // search for all event handlers
334 ModulePathMap& rPathMap = maEventPaths[ rModuleName ];
335 for( EventHandlerInfoMap::iterator aIt = maEventInfos.begin(), aEnd = maEventInfos.end(); aIt != aEnd; ++aIt )
336 {
337 const EventHandlerInfo& rInfo = aIt->second;
338 if( rInfo.mnModuleType == nModuleType )
339 rPathMap[ rInfo.mnEventId ] = resolveVBAMacro( mpShell, maLibraryName, rModuleName, rInfo.maMacroName );
340 }
341 return rPathMap;
342 }
343
344 // ============================================================================
345