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

Generated by: LCOV version 1.10