LCOV - code coverage report
Current view: top level - vbahelper/source/vbahelper - vbaeventshelperbase.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 109 148 73.6 %
Date: 2015-06-13 12:38:46 Functions: 14 18 77.8 %
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          29 : VbaEventsHelperBase::VbaEventsHelperBase( const uno::Sequence< uno::Any >& rArgs, const uno::Reference< uno::XComponentContext >& /*xContext*/ ) :
      34             :     mpShell( 0 ),
      35          29 :     mbDisposed( true )
      36             : {
      37             :     try
      38             :     {
      39          29 :         mxModel = getXSomethingFromArgs< frame::XModel >( rArgs, 0, false );
      40          28 :         mpShell = getSfxObjShell( mxModel );
      41             :     }
      42           1 :     catch( uno::Exception& )
      43             :     {
      44             :     }
      45          29 :     mbDisposed = mpShell == 0;
      46          29 :     startListening();
      47          29 : }
      48             : 
      49           1 : VbaEventsHelperBase::~VbaEventsHelperBase()
      50             : {
      51             :     SAL_WARN_IF( !mbDisposed, "vbahelper", "VbaEventsHelperBase::~VbaEventsHelperBase - missing disposing notification" );
      52           1 : }
      53             : 
      54        2677 : 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        2677 :     EventQueue aEventQueue;
      61        2677 :     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        2677 :     bool bCancel = false;
      67             : 
      68             :     /*  bExecuted will change to true if at least one event handler has been
      69             :         found and executed. */
      70        2677 :     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        8967 :     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        3621 :         if( mbDisposed || !mxModel.is() || !mpShell )
      81           0 :             throw uno::RuntimeException();
      82             : 
      83             :         // get info for next event
      84        3621 :         const EventHandlerInfo& rInfo = getEventHandlerInfo( aEventQueue.front().mnEventId );
      85        3621 :         uno::Sequence< uno::Any > aEventArgs = aEventQueue.front().maArgs;
      86        3621 :         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        3621 :         if( implPrepareEvent( aEventQueue, rInfo, aEventArgs ) )
      93             :         {
      94             :             // search the event handler macro in the document
      95        2151 :             OUString aMacroPath = getEventHandlerPath( rInfo, aEventArgs );
      96        2143 :             if( !aMacroPath.isEmpty() )
      97             :             {
      98             :                 // build the argument list
      99          35 :                 uno::Sequence< uno::Any > aVbaArgs = implBuildArgumentList( rInfo, aEventArgs );
     100             :                 // insert current cancel value
     101          35 :                 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          70 :                 uno::Any aRet, aCaller;
     109          35 :                 executeMacro( mpShell, aMacroPath, aVbaArgs, aRet, aCaller );
     110             :                 // extract new cancel value (may be boolean or any integer type)
     111          35 :                 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          70 :                 bExecuted = true;
     118        2143 :             }
     119             :         }
     120             :         // post processing (also, if event handler does not exist, or disabled, or on error
     121        3613 :         implPostProcessEvent( aEventQueue, rInfo, bCancel );
     122        3621 :     }
     123             : 
     124             :     // if event handlers want to cancel the event, do so regardless of any errors
     125        2669 :     if( bCancel )
     126           0 :         throw util::VetoException();
     127             : 
     128             :     // return true, if at least one event handler has been found
     129        2677 :     return bExecuted;
     130             : }
     131             : 
     132         275 : void SAL_CALL VbaEventsHelperBase::notifyEvent( const document::EventObject& rEvent ) throw (uno::RuntimeException, std::exception)
     133             : {
     134             :     SAL_INFO("vbahelper", "VbaEventsHelperBase::notifyEvent( \"" << rEvent.EventName << "\" )");
     135         275 :     if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ) )
     136          28 :         stopListening();
     137         275 : }
     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           0 : sal_Bool VbaEventsHelperBase::supportsService(OUString const & ServiceName)
     185             :     throw (css::uno::RuntimeException, std::exception)
     186             : {
     187           0 :     return cppu::supportsService(this, ServiceName);
     188             : }
     189             : 
     190         364 : void VbaEventsHelperBase::processVbaEventNoThrow( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
     191             : {
     192             :     try
     193             :     {
     194         364 :         processVbaEvent( nEventId, rArgs );
     195             :     }
     196           0 :     catch( uno::Exception& )
     197             :     {
     198             :     }
     199         364 : }
     200             : 
     201             : // protected ------------------------------------------------------------------
     202             : 
     203         812 : void VbaEventsHelperBase::registerEventHandler( sal_Int32 nEventId, sal_Int32 nModuleType,
     204             :         const sal_Char* pcMacroName, sal_Int32 nCancelIndex, const uno::Any& rUserData )
     205             : {
     206         812 :     EventHandlerInfo& rInfo = maEventInfos[ nEventId ];
     207         812 :     rInfo.mnEventId = nEventId;
     208         812 :     rInfo.mnModuleType = nModuleType;
     209         812 :     rInfo.maMacroName = OUString::createFromAscii( pcMacroName );
     210         812 :     rInfo.mnCancelIndex = nCancelIndex;
     211         812 :     rInfo.maUserData = rUserData;
     212         812 : }
     213             : 
     214             : // private --------------------------------------------------------------------
     215             : 
     216          29 : void VbaEventsHelperBase::startListening()
     217             : {
     218          29 :     if( mbDisposed )
     219          30 :         return;
     220             : 
     221          28 :     uno::Reference< document::XEventBroadcaster > xEventBroadcaster( mxModel, uno::UNO_QUERY );
     222          28 :     if( xEventBroadcaster.is() )
     223          28 :         try { xEventBroadcaster->addEventListener( this ); } catch( uno::Exception& ) {}
     224             : }
     225             : 
     226          28 : void VbaEventsHelperBase::stopListening()
     227             : {
     228          28 :     if( mbDisposed )
     229          28 :         return;
     230             : 
     231          28 :     uno::Reference< document::XEventBroadcaster > xEventBroadcaster( mxModel, uno::UNO_QUERY );
     232          28 :     if( xEventBroadcaster.is() )
     233          28 :         try { xEventBroadcaster->removeEventListener( this ); } catch( uno::Exception& ) {}
     234             : 
     235          28 :     mxModel.clear();
     236          28 :     mpShell = 0;
     237          28 :     maEventInfos.clear();
     238          28 :     mbDisposed = true;
     239             : }
     240             : 
     241         694 : sal_Bool SAL_CALL VbaEventsHelperBase::hasVbaEventHandler( sal_Int32 nEventId, const uno::Sequence< uno::Any >& rArgs )
     242             :         throw (lang::IllegalArgumentException, uno::RuntimeException, std::exception)
     243             : {
     244         694 :     EventHandlerInfoMap::const_iterator aIt = maEventInfos.find( nEventId );
     245         694 :     if( aIt == maEventInfos.end() )
     246           0 :         return sal_False; // throwing a lot of exceptions is slow.
     247             :     else // getEventHandlerPath() searches for the macro in the document
     248         694 :         return !getEventHandlerPath( aIt->second, rArgs ).isEmpty();
     249             : }
     250             : 
     251        3621 : const VbaEventsHelperBase::EventHandlerInfo& VbaEventsHelperBase::getEventHandlerInfo(
     252             :         sal_Int32 nEventId ) const throw (lang::IllegalArgumentException)
     253             : {
     254        3621 :     EventHandlerInfoMap::const_iterator aIt = maEventInfos.find( nEventId );
     255        3621 :     if( aIt == maEventInfos.end() )
     256           0 :         throw lang::IllegalArgumentException();
     257        3621 :     return aIt->second;
     258             : }
     259             : 
     260        2845 : OUString VbaEventsHelperBase::getEventHandlerPath( const EventHandlerInfo& rInfo,
     261             :         const uno::Sequence< uno::Any >& rArgs ) throw (lang::IllegalArgumentException, uno::RuntimeException)
     262             : {
     263        2845 :     OUString aModuleName;
     264        2845 :     switch( rInfo.mnModuleType )
     265             :     {
     266             :         // global event handlers may exist in any standard code module
     267             :         case script::ModuleType::NORMAL:
     268          28 :         break;
     269             : 
     270             :         // document event: get name of the code module associated to the event sender
     271             :         case script::ModuleType::DOCUMENT:
     272        2817 :             aModuleName = implGetDocumentModuleName( rInfo, rArgs );
     273        2809 :             if( aModuleName.isEmpty() )
     274           0 :                 throw lang::IllegalArgumentException();
     275        2809 :         break;
     276             : 
     277             :         default:
     278           0 :             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        2837 :     EventHandlerPathMap::iterator aIt = maEventPaths.find( aModuleName );
     284        2881 :     ModulePathMap& rPathMap = (aIt == maEventPaths.end()) ? updateModulePathMap( aModuleName ) : aIt->second;
     285        2845 :     return rPathMap[ rInfo.mnEventId ];
     286             : }
     287             : 
     288         128 : void VbaEventsHelperBase::ensureVBALibrary() throw (uno::RuntimeException)
     289             : {
     290         128 :     if( !mxModuleInfos.is() ) try
     291             :     {
     292          28 :         maLibraryName = getDefaultProjectName( mpShell );
     293          28 :         if( maLibraryName.isEmpty() )
     294           0 :             throw uno::RuntimeException();
     295          28 :         uno::Reference< beans::XPropertySet > xModelProps( mxModel, uno::UNO_QUERY_THROW );
     296          28 :         uno::Reference< container::XNameAccess > xBasicLibs( xModelProps->getPropertyValue(
     297          56 :             "BasicLibraries" ), uno::UNO_QUERY_THROW );
     298          28 :         mxModuleInfos.set( xBasicLibs->getByName( maLibraryName ), uno::UNO_QUERY_THROW );
     299             :         // listen to changes in the VBA source code
     300          56 :         uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModuleInfos, uno::UNO_QUERY_THROW );
     301          56 :         xChangesNotifier->addChangesListener( this );
     302             :     }
     303           0 :     catch( uno::Exception& )
     304             :     {
     305             :         // error accessing the Basic library, so this object is useless
     306           0 :         stopListening();
     307           0 :         throw uno::RuntimeException();
     308             :     }
     309         128 : }
     310             : 
     311         128 : sal_Int32 VbaEventsHelperBase::getModuleType( const OUString& rModuleName ) throw (uno::RuntimeException)
     312             : {
     313             :     // make sure the VBA library exists
     314         128 :     ensureVBALibrary();
     315             : 
     316             :     // no module specified: global event handler in standard code modules
     317         128 :     if( rModuleName.isEmpty() )
     318          27 :         return script::ModuleType::NORMAL;
     319             : 
     320             :     // get module type from module info
     321             :     try
     322             :     {
     323         101 :         return mxModuleInfos->getModuleInfo( rModuleName ).ModuleType;
     324             :     }
     325          44 :     catch( uno::Exception& )
     326             :     {
     327             :     }
     328          88 :     throw uno::RuntimeException();
     329             : }
     330             : 
     331         128 : VbaEventsHelperBase::ModulePathMap& VbaEventsHelperBase::updateModulePathMap( const OUString& rModuleName ) throw (uno::RuntimeException)
     332             : {
     333             :     // get type of the specified module (throws on error)
     334         128 :     sal_Int32 nModuleType = getModuleType( rModuleName );
     335             :     // search for all event handlers
     336          84 :     ModulePathMap& rPathMap = maEventPaths[ rModuleName ];
     337        2520 :     for( EventHandlerInfoMap::iterator aIt = maEventInfos.begin(), aEnd = maEventInfos.end(); aIt != aEnd; ++aIt )
     338             :     {
     339        2436 :         const EventHandlerInfo& rInfo = aIt->second;
     340        2436 :         if( rInfo.mnModuleType == nModuleType )
     341        1593 :             rPathMap[ rInfo.mnEventId ] = resolveVBAMacro( mpShell, maLibraryName, rModuleName, rInfo.maMacroName );
     342             :     }
     343          84 :     return rPathMap;
     344             : }
     345             : 
     346             : 
     347             : 
     348             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11