LCOV - code coverage report
Current view: top level - vbahelper/source/vbahelper - vbaeventshelperbase.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 107 147 72.8 %
Date: 2014-11-03 Functions: 15 19 78.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10