xref: /trunk/main/vbahelper/source/vbahelper/vbaeventshelperbase.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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