LCOV - code coverage report
Current view: top level - vbahelper/source/vbahelper - vbaeventshelperbase.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 104 142 73.2 %
Date: 2012-08-25 Functions: 14 17 82.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 95 271 35.1 %

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

Generated by: LCOV version 1.10