diff options
-rwxr-xr-x | framework/inc/framework/iguard.hxx | 69 | ||||
-rwxr-xr-x | framework/inc/helper/undomanagerhelper.hxx | 85 | ||||
-rw-r--r-- | framework/prj/d.lst | 1 | ||||
-rwxr-xr-x | framework/source/helper/undomanagerhelper.cxx | 828 | ||||
-rw-r--r-- | framework/util/makefile.mk | 1 | ||||
-rw-r--r-- | sfx2/inc/sfx2/sfxbasemodel.hxx | 7 | ||||
-rwxr-xr-x | sfx2/qa/complex/sfx2/UndoManager.java | 3 | ||||
-rwxr-xr-x | sfx2/source/doc/docundomanager.cxx | 121 |
8 files changed, 798 insertions, 317 deletions
diff --git a/framework/inc/framework/iguard.hxx b/framework/inc/framework/iguard.hxx new file mode 100755 index 000000000000..7c00858b208d --- /dev/null +++ b/framework/inc/framework/iguard.hxx @@ -0,0 +1,69 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef __FRAMEWORK_THREADHELP_IGUARD_H_ +#define __FRAMEWORK_THREADHELP_IGUARD_H_ + +//_________________________________________________________________________________________________________________ +// includes +//_________________________________________________________________________________________________________________ + +#include <sal/types.h> + +//_________________________________________________________________________________________________________________ +// namespace +//_________________________________________________________________________________________________________________ + +namespace framework{ + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +/*-************************************************************************************************************//** + @descr interface for guarding a lock +*//*-*************************************************************************************************************/ +class SAL_NO_VTABLE IGuard +{ + //------------------------------------------------------------------------------------------------------------- + // public methods + //------------------------------------------------------------------------------------------------------------- + public: + + /** clears the lock. If the guard does not currently hold the lock, nothing happens. + */ + virtual void clear() = 0; + + /** attempts to re-establishes the lock, blocking until the attempt is successful. + */ + virtual void reset() = 0; + +}; // class IGuard + +} // namespace framework + +#endif // #ifndef __FRAMEWORK_THREADHELP_IGUARD_H_ diff --git a/framework/inc/helper/undomanagerhelper.hxx b/framework/inc/helper/undomanagerhelper.hxx index 98503e08144f..462aaa740af3 100755 --- a/framework/inc/helper/undomanagerhelper.hxx +++ b/framework/inc/helper/undomanagerhelper.hxx @@ -27,16 +27,14 @@ #ifndef FRAMEWORK_UNDOMANAGERHELPER_HXX #define FRAMEWORK_UNDOMANAGERHELPER_HXX +#include "framework/iguard.hxx" +#include "framework/imutex.hxx" + /** === begin UNO includes === **/ #include <com/sun/star/document/XUndoManager.hpp> /** === end UNO includes === **/ -#include <boost/scoped_ptr.hpp> - -namespace osl -{ - class Mutex; -} +#include <rtl/ref.hxx> namespace svl { @@ -49,17 +47,24 @@ namespace framework //...................................................................................................................... //================================================================================================================== - //= IUndoManagerImplementation + //= IMutexGuard //================================================================================================================== - class SAL_NO_VTABLE IUndoManagerImplementation + class SAL_NO_VTABLE IMutexGuard : public IGuard { public: - /** returns the mutex which is protecting the instance. Needed for listener administration synchronization. + /** returns the mutex guarded by the instance. - Note that the mutex will <em>not</em> be used for multi-threading safety of the UndoManagerHelper. + Even if the guard currently has not a lock on the mutex, this method must succeed. */ - virtual ::osl::Mutex& getMutex() = 0; + virtual IMutex& getGuardedMutex() = 0; + }; + //================================================================================================================== + //= IUndoManagerImplementation + //================================================================================================================== + class SAL_NO_VTABLE IUndoManagerImplementation + { + public: /** returns the IUndoManager interface to the actual Undo stack @throws com::sun::star::lang::DisposedException @@ -77,29 +82,31 @@ namespace framework }; //================================================================================================================== - //= IClearableInstanceLock - //================================================================================================================== - /** helper class for releasing a lock - - Since clients of UndoManagerHelper are responsible for locking their instance, but the UndoManagerHelper - needs to notify its listeners, and this needs to happen without any instance lock, all affected methods - take an IClearableInstanceLock parameter, to be able to clear the owner's lock before doing any notifications. - */ - class SAL_NO_VTABLE IClearableInstanceLock - { - public: - virtual void clear() = 0; - }; - - //================================================================================================================== //= UndoManagerHelper //================================================================================================================== class UndoManagerHelper_Impl; /** helper class for implementing an XUndoManager - The class defines the same methods as an XUndoManager does, but lacks certain aspects of a full-blown UNO - component. In particular, it is the responsibility of the owner of the instance to care for multi-threading - safety, and for disposal checks. + Several of the methods of the class take an IMutexGuard instance. It is assumed that this guard has a lock on + its mutext at the moment the method is entered. The lock will be released before any notifications to the + registered XUndoManagerListeners happen. + + The following locking strategy is used for this mutex: + <ul><li>Any notifications to the registered XUndoManagerListeners are after the guard has been cleared. i.e. + without the mutex being locked.</p> + <li>Any calls into the <code>IUndoManager</code> implementation is made without the mutex being locked. + Note that this implies that the <code>IUndoManager</code> implementation must be thread-safe in itself + (which is true for the default implementation, SfxUndoManager).</li> + <li>An exception to the previous item are the <member>IUndoManager::Undo</member> and + <member>IUndoManager::Redo</member> methods: They're called with the given external mutex being + locked.</li> + </ul> + + The reason for the exception for IUndoManager::Undo and IUndoManager::Redo is that those are expected to + modify the actual document which the UndoManager works for. And as long as our documents are not thread-safe, + and as long as we do not re-fit <strong>all</strong> existing SfxUndoImplementations to <em>not</em> expect + the dreaded SolarMutex being locked when they're called, the above behavior is a compromise between "how it should + be" and "how it can realistically be". */ class UndoManagerHelper { @@ -111,12 +118,12 @@ namespace framework void disposing(); // XUndoManager equivalents - void enterUndoContext( const ::rtl::OUString& i_title, IClearableInstanceLock& i_instanceLock ); - void enterHiddenUndoContext( IClearableInstanceLock& i_instanceLock ); - void leaveUndoContext( IClearableInstanceLock& i_instanceLock ); - void addUndoAction( const ::com::sun::star::uno::Reference< ::com::sun::star::document::XUndoAction >& i_action, IClearableInstanceLock& i_instanceLock ); - void undo( IClearableInstanceLock& i_instanceLock ); - void redo( IClearableInstanceLock& i_instanceLock ); + void enterUndoContext( const ::rtl::OUString& i_title, IMutexGuard& i_instanceLock ); + void enterHiddenUndoContext( IMutexGuard& i_instanceLock ); + void leaveUndoContext( IMutexGuard& i_instanceLock ); + void addUndoAction( const ::com::sun::star::uno::Reference< ::com::sun::star::document::XUndoAction >& i_action, IMutexGuard& i_instanceLock ); + void undo( IMutexGuard& i_instanceLock ); + void redo( IMutexGuard& i_instanceLock ); ::sal_Bool isUndoPossible() const; ::sal_Bool isRedoPossible() const; ::rtl::OUString getCurrentUndoActionTitle() const; @@ -125,9 +132,9 @@ namespace framework getAllUndoActionTitles() const; ::com::sun::star::uno::Sequence< ::rtl::OUString > getAllRedoActionTitles() const; - void clear( IClearableInstanceLock& i_instanceLock ); - void clearRedo( IClearableInstanceLock& i_instanceLock ); - void reset( IClearableInstanceLock& i_instanceLock ); + void clear( IMutexGuard& i_instanceLock ); + void clearRedo( IMutexGuard& i_instanceLock ); + void reset( IMutexGuard& i_instanceLock ); void addUndoManagerListener( const ::com::sun::star::uno::Reference< ::com::sun::star::document::XUndoManagerListener >& i_listener ); void removeUndoManagerListener( const ::com::sun::star::uno::Reference< ::com::sun::star::document::XUndoManagerListener >& i_listener ); @@ -137,7 +144,7 @@ namespace framework ::sal_Bool isLocked(); private: - ::boost::scoped_ptr< UndoManagerHelper_Impl > m_pImpl; + ::rtl::Reference< UndoManagerHelper_Impl > m_pImpl; }; //...................................................................................................................... diff --git a/framework/prj/d.lst b/framework/prj/d.lst index b7d996eec53f..a3c3f382da1e 100644 --- a/framework/prj/d.lst +++ b/framework/prj/d.lst @@ -46,6 +46,7 @@ mkdir: %_DEST%\xml%_EXT%\uiconfig\modules\StartModule\statusbar ..\inc\helper\documentundoguard.hxx %_DEST%\inc%_EXT%\framework\documentundoguard.hxx ..\inc\helper\undomanagerhelper.hxx %_DEST%\inc%_EXT%\framework\undomanagerhelper.hxx ..\inc\framework\imutex.hxx %_DEST%\inc%_EXT%\framework\imutex.hxx +..\inc\framework\iguard.hxx %_DEST%\inc%_EXT%\framework\iguard.hxx ..\uiconfig\startmodule\menubar\*.xml %_DEST%\xml%_EXT%\uiconfig\modules\StartModule\menubar\*.xml ..\uiconfig\startmodule\toolbar\*.xml %_DEST%\xml%_EXT%\uiconfig\modules\StartModule\toolbar\*.xml diff --git a/framework/source/helper/undomanagerhelper.cxx b/framework/source/helper/undomanagerhelper.cxx index f7f297f6cdee..81b1a30c289c 100755 --- a/framework/source/helper/undomanagerhelper.cxx +++ b/framework/source/helper/undomanagerhelper.cxx @@ -33,11 +33,15 @@ /** === end UNO includes === **/ #include <cppuhelper/interfacecontainer.hxx> +#include <cppuhelper/exc_hlp.hxx> #include <comphelper/flagguard.hxx> +#include <comphelper/asyncnotification.hxx> #include <svl/undo.hxx> #include <tools/diagnose_ex.h> +#include <osl/conditn.hxx> #include <stack> +#include <boost/function.hpp> //...................................................................................................................... namespace framework @@ -148,55 +152,108 @@ namespace framework } //================================================================================================================== - //= UndoManagerHelper_Impl + //= UndoManagerRequest //================================================================================================================== - class UndoManagerHelper_Impl : public SfxUndoListener + class UndoManagerRequest : public ::comphelper::AnyEvent { public: - ::cppu::OInterfaceContainerHelper aUndoListeners; - IUndoManagerImplementation& rUndoManagerImplementation; - UndoManagerHelper& rAntiImpl; - bool bAPIActionRunning; - ::std::stack< bool > aContextVisibilities; -#if OSL_DEBUG_LEVEL > 0 - ::std::stack< bool > aContextAPIFlags; -#endif + UndoManagerRequest( ::boost::function0< void > const& i_request ) + :m_request( i_request ) + ,m_caughtException() + ,m_finishCondition() + { + m_finishCondition.reset(); + } - UndoManagerHelper_Impl( UndoManagerHelper& i_antiImpl, IUndoManagerImplementation& i_undoManagerImpl ) - :aUndoListeners( i_undoManagerImpl.getMutex() ) - ,rUndoManagerImplementation( i_undoManagerImpl ) - ,rAntiImpl( i_antiImpl ) - ,bAPIActionRunning( false ) + void execute() { - getUndoManager().AddUndoListener( *this ); + try + { + m_request(); + } + catch( const Exception& ) + { + m_caughtException = ::cppu::getCaughtException(); + } + m_finishCondition.set(); } - virtual ~UndoManagerHelper_Impl() + void wait() { + m_finishCondition.wait(); + if ( m_caughtException.hasValue() ) + ::cppu::throwException( m_caughtException ); } - //.............................................................................................................. - IUndoManager& getUndoManager() + protected: + ~UndoManagerRequest() { - return rUndoManagerImplementation.getImplUndoManager(); } - //.............................................................................................................. - Reference< XUndoManager > getXUndoManager() + private: + ::boost::function0< void > m_request; + Any m_caughtException; + ::osl::Condition m_finishCondition; + }; + + //------------------------------------------------------------------------------------------------------------------ + + //================================================================================================================== + //= UndoManagerHelper_Impl + //================================================================================================================== + class UndoManagerHelper_Impl :public SfxUndoListener + ,public ::comphelper::IEventProcessor + { + private: + ::osl::Mutex m_aMutex; + oslInterlockedCount m_refCount; + ::rtl::Reference< ::comphelper::AsyncEventNotifier > + m_pRequestProcessor; + bool m_disposed; + bool m_bAPIActionRunning; + ::cppu::OInterfaceContainerHelper m_aUndoListeners; + IUndoManagerImplementation& m_rUndoManagerImplementation; + UndoManagerHelper& m_rAntiImpl; + ::std::stack< bool > m_aContextVisibilities; +#if OSL_DEBUG_LEVEL > 0 + ::std::stack< bool > m_aContextAPIFlags; +#endif + + public: + ::osl::Mutex& getMutex() { return m_aMutex; } + + public: + UndoManagerHelper_Impl( UndoManagerHelper& i_antiImpl, IUndoManagerImplementation& i_undoManagerImpl ) + :m_aMutex() + ,m_refCount( 0 ) + ,m_pRequestProcessor() + ,m_disposed( false ) + ,m_bAPIActionRunning( false ) + ,m_aUndoListeners( m_aMutex ) + ,m_rUndoManagerImplementation( i_undoManagerImpl ) + ,m_rAntiImpl( i_antiImpl ) { - return rUndoManagerImplementation.getThis(); + getUndoManager().AddUndoListener( *this ); } //.............................................................................................................. - void disposing() + IUndoManager& getUndoManager() const { - EventObject aEvent; - aEvent.Source = getXUndoManager(); - aUndoListeners.disposeAndClear( aEvent ); + return m_rUndoManagerImplementation.getImplUndoManager(); + } - getUndoManager().RemoveUndoListener( *this ); + //.............................................................................................................. + Reference< XUndoManager > getXUndoManager() const + { + return m_rUndoManagerImplementation.getThis(); } + //.............................................................................................................. + // ::comphelper::IEventProcessor + virtual void SAL_CALL acquire(); + virtual void SAL_CALL release(); + virtual void processEvent( const ::comphelper::AnyEvent& _rEvent ); + // SfxUndoListener virtual void actionUndone( const String& i_actionComment ); virtual void actionRedone( const String& i_actionComment ); @@ -204,67 +261,114 @@ namespace framework virtual void cleared(); virtual void clearedRedo(); virtual void listActionEntered( const String& i_comment ); - virtual void listActionLeft(); + virtual void listActionLeft( const String& i_comment ); virtual void listActionLeftAndMerged(); virtual void listActionCancelled(); virtual void undoManagerDying(); // public operations - void enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden, IClearableInstanceLock& i_instanceLock ); - - void doUndoRedo( - USHORT ( ::svl::IUndoManager::*i_checkMethod )( bool const ) const, - BOOL ( ::svl::IUndoManager::*i_doMethod )(), - UniString ( ::svl::IUndoManager::*i_titleRetriever )( USHORT, bool const ) const, - void ( SAL_CALL ::com::sun::star::document::XUndoManagerListener::*i_notificationMethod )( const ::com::sun::star::document::UndoManagerEvent& ), - IClearableInstanceLock& i_instanceLock - ); - void notify( ::rtl::OUString const& i_title, - void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ), - IClearableInstanceLock& i_instanceLock - ); - void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ), - IClearableInstanceLock& i_instanceLock - ); + void disposing(); + + void enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock ); + void leaveUndoContext( IMutexGuard& i_instanceLock ); + void addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock ); + void undo( IMutexGuard& i_instanceLock ); + void redo( IMutexGuard& i_instanceLock ); + void clear( IMutexGuard& i_instanceLock ); + void clearRedo( IMutexGuard& i_instanceLock ); + void reset( IMutexGuard& i_instanceLock ); + + void addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) + { + m_aUndoListeners.addInterface( i_listener ); + } + + void removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) + { + m_aUndoListeners.removeInterface( i_listener ); + } + + UndoManagerEvent + buildEvent( ::rtl::OUString const& i_title ) const; + void notify( ::rtl::OUString const& i_title, void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) ); - void notify( - void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) - ); + void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) ) + { + notify( ::rtl::OUString(), i_notificationMethod ); + } + + void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) ); + + private: + virtual ~UndoManagerHelper_Impl() + { + } + + /// adds a function to be called to the request processor's queue + void impl_processRequest( ::boost::function0< void > const& i_request, IMutexGuard& i_instanceLock ); + + /// impl-versions of the XUndoManager API. Those methods are executed in the dedicated thread defined by m_pRequestProcessor + void impl_enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden ); + void impl_leaveUndoContext(); + void impl_addUndoAction( const Reference< XUndoAction >& i_action ); + void impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo ); + void impl_clear(); + void impl_clearRedo(); + void impl_reset(); }; //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper_Impl::notify( ::rtl::OUString const& i_title, void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ), - IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper_Impl::acquire() { - UndoManagerEvent aEvent; - aEvent.Source = getXUndoManager(); - aEvent.UndoActionTitle = i_title; - aEvent.UndoContextDepth = getUndoManager().GetListActionDepth(); + osl_incrementInterlockedCount( &m_refCount ); + } - i_instanceLock.clear(); - aUndoListeners.notifyEach( i_notificationMethod, aEvent ); + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::release() + { + if ( 0 == osl_decrementInterlockedCount( &m_refCount ) ) + delete this; } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper_Impl::notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ), - IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper_Impl::disposing() { EventObject aEvent; aEvent.Source = getXUndoManager(); - i_instanceLock.clear(); - aUndoListeners.notifyEach( i_notificationMethod, aEvent ); + m_aUndoListeners.disposeAndClear( aEvent ); + + ::osl::MutexGuard aGuard( m_aMutex ); + + getUndoManager().RemoveUndoListener( *this ); + + if ( m_pRequestProcessor.is() ) + { + m_pRequestProcessor->removeEventsForProcessor( this ); + m_pRequestProcessor->terminate(); + m_pRequestProcessor->join(); + m_pRequestProcessor.clear(); + } + + m_disposed = true; } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper_Impl::notify( ::rtl::OUString const& i_title, - void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) ) + UndoManagerEvent UndoManagerHelper_Impl::buildEvent( ::rtl::OUString const& i_title ) const { UndoManagerEvent aEvent; aEvent.Source = getXUndoManager(); aEvent.UndoActionTitle = i_title; aEvent.UndoContextDepth = getUndoManager().GetListActionDepth(); + return aEvent; + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::notify( ::rtl::OUString const& i_title, + void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) ) + { + const UndoManagerEvent aEvent( buildEvent( i_title ) ); // TODO: this notification method here is used by UndoManagerHelper_Impl, to multiplex the notifications we // receive from the IUndoManager. Those notitications are sent with a locked SolarMutex, which means @@ -272,24 +376,131 @@ namespace framework // Fixing this properly would require outsourcing all the notifications into an own thread - which might lead // to problems of its own, since clients might expect synchronous notifications. - aUndoListeners.notifyEach( i_notificationMethod, aEvent ); + m_aUndoListeners.notifyEach( i_notificationMethod, aEvent ); } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper_Impl::notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) ) { - EventObject aEvent; - aEvent.Source = getXUndoManager(); + const EventObject aEvent( getXUndoManager() ); // TODO: the same comment as in the other notify, regarding SM locking applies here ... - aUndoListeners.notifyEach( i_notificationMethod, aEvent ); + m_aUndoListeners.notifyEach( i_notificationMethod, aEvent ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock ) + { + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_enterUndoContext, + this, + ::boost::cref( i_title ), + i_hidden + ), + i_instanceLock + ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper_Impl::enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden, IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_leaveUndoContext, + this + ), + i_instanceLock + ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock ) + { + if ( !i_action.is() ) + throw IllegalArgumentException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "illegal undo action object" ) ), + getXUndoManager(), + 1 + ); + + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_addUndoAction, + this, + ::boost::ref( i_action ) + ), + i_instanceLock + ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::clear( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_clear, + this + ), + i_instanceLock + ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::clearRedo( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_clearRedo, + this + ), + i_instanceLock + ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::reset( IMutexGuard& i_instanceLock ) + { + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_reset, + this + ), + i_instanceLock + ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::impl_processRequest( ::boost::function0< void > const& i_request, IMutexGuard& i_instanceLock ) + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + if ( !m_pRequestProcessor.is() ) + { + m_pRequestProcessor.set( new ::comphelper::AsyncEventNotifier ); + m_pRequestProcessor->create(); + } + + ::rtl::Reference< UndoManagerRequest > pRequest( new UndoManagerRequest( i_request ) ); + m_pRequestProcessor->addEvent( pRequest.get(), this ); + + aGuard.clear(); + i_instanceLock.clear(); + // <--- SYNCHRONIZED + + pRequest->wait(); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::impl_enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden ) { // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( ::osl::Thread::getCurrentIdentifier() == m_pRequestProcessor->getIdentifier(), + "UndoManagerHelper_Impl::impl_enterUndoContext: expected to be executed serialized, in a dedicated thread!" ); + IUndoManager& rUndoManager = getUndoManager(); if ( !rUndoManager.IsUndoEnabled() ) // ignore this request if the manager is locked @@ -298,78 +509,268 @@ namespace framework if ( i_hidden && ( rUndoManager.GetUndoActionCount( IUndoManager::CurrentLevel ) == 0 ) ) throw EmptyUndoStackException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "can't enter a hidden context without a previous Undo action" ) ), - rUndoManagerImplementation.getThis() + m_rUndoManagerImplementation.getThis() ); { - ::comphelper::FlagGuard aNotificationGuard( bAPIActionRunning ); + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); rUndoManager.EnterListAction( i_title, ::rtl::OUString() ); } - aContextVisibilities.push( i_hidden ); + m_aContextVisibilities.push( i_hidden ); - notify( i_title, i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, i_instanceLock ); + const UndoManagerEvent aEvent( buildEvent( i_title ) ); + aGuard.clear(); // <--- SYNCHRONIZED + + m_aUndoListeners.notifyEach( i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, aEvent ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper_Impl::doUndoRedo( - USHORT ( IUndoManager::*i_checkMethod )( bool const ) const, BOOL ( IUndoManager::*i_doMethod )(), - String ( IUndoManager::*i_titleRetriever )( USHORT, bool const ) const, - void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ), - IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper_Impl::impl_leaveUndoContext() { // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( ::osl::Thread::getCurrentIdentifier() == m_pRequestProcessor->getIdentifier(), + "UndoManagerHelper_Impl::impl_leaveUndoContext: expected to be executed serialized, in a dedicated thread!" ); + + IUndoManager& rUndoManager = getUndoManager(); + if ( !rUndoManager.IsUndoEnabled() ) + // ignore this request if the manager is locked + return; + + if ( !rUndoManager.IsInListAction() ) + throw InvalidStateException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "no active undo context" ) ), + getXUndoManager() + ); + + USHORT nContextElements = 0; + + const bool isHiddenContext = m_aContextVisibilities.top();; + m_aContextVisibilities.pop(); + + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + if ( isHiddenContext ) + nContextElements = rUndoManager.LeaveAndMergeListAction(); + else + nContextElements = rUndoManager.LeaveListAction(); + } + + // prepare notification + void ( SAL_CALL XUndoManagerListener::*notificationMethod )( const UndoManagerEvent& ) = NULL; + + UndoManagerEvent aEvent( buildEvent( ::rtl::OUString() ) ); + if ( nContextElements == 0 ) + { + notificationMethod = &XUndoManagerListener::cancelledContext; + } + else if ( isHiddenContext ) + { + notificationMethod = &XUndoManagerListener::leftHiddenContext; + } + else + { + aEvent.UndoActionTitle = rUndoManager.GetUndoActionComment( 0, IUndoManager::CurrentLevel ); + notificationMethod = &XUndoManagerListener::leftContext; + } + + aGuard.clear(); + // <--- SYNCHRONIZED + + m_aUndoListeners.notifyEach( notificationMethod, aEvent ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo ) + { + ::osl::Guard< ::framework::IMutex > aExternalGuard( i_externalLock.getGuardedMutex() ); + // note that this assumes that the mutex has been released in the thread which added the + // Undo/Redo request, so we can successfully acquire it + + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( ::osl::Thread::getCurrentIdentifier() == m_pRequestProcessor->getIdentifier(), + "UndoManagerHelper_Impl::impl_doUndoRedo: expected to be executed serialized, in a dedicated thread!" ); + IUndoManager& rUndoManager = getUndoManager(); if ( rUndoManager.IsInListAction() ) throw UndoContextNotClosedException( ::rtl::OUString(), getXUndoManager() ); - if ( (rUndoManager.*i_checkMethod)( IUndoManager::TopLevel ) == 0 ) + const USHORT nElements = i_undo + ? rUndoManager.GetUndoActionCount( IUndoManager::TopLevel ) + : rUndoManager.GetRedoActionCount( IUndoManager::TopLevel ); + if ( nElements == 0 ) throw EmptyUndoStackException( ::rtl::OUString::createFromAscii( "stack is empty" ), getXUndoManager() ); - const ::rtl::OUString sUndoActionTitle = (rUndoManager.*i_titleRetriever)( 0, IUndoManager::TopLevel ); + aGuard.clear(); + // <--- SYNCHRONIZED + + try { - ::comphelper::FlagGuard aNotificationGuard( bAPIActionRunning ); - try - { - (rUndoManager.*i_doMethod)(); - } - catch( const RuntimeException& ) { /* allowed to leave here */ throw; } - catch( const UndoFailedException& ) { /* allowed to leave here */ throw; } - catch( const Exception& ) - { - // not allowed to leave - const Any aError( ::cppu::getCaughtException() ); - throw UndoFailedException( ::rtl::OUString(), getXUndoManager(), aError ); - } + if ( i_undo ) + rUndoManager.Undo(); + else + rUndoManager.Redo(); + } + catch( const RuntimeException& ) { /* allowed to leave here */ throw; } + catch( const UndoFailedException& ) { /* allowed to leave here */ throw; } + catch( const Exception& ) + { + // not allowed to leave + const Any aError( ::cppu::getCaughtException() ); + throw UndoFailedException( ::rtl::OUString(), getXUndoManager(), aError ); } - notify( sUndoActionTitle, i_notificationMethod, i_instanceLock ); - // <--- SYNCHRONIZED + // note that in opposite to all of the other methods, we do *not* have our mutex locked when calling + // into the IUndoManager implementation. This ensures that an actual XUndoAction::undo/redo is also + // called without our mutex being locked. + // As a consequence, we do not set m_bAPIActionRunning here. Instead, our actionUndone/actionRedone methods + // *always* multiplex the event to our XUndoManagerListeners, not only when m_bAPIActionRunning is FALSE (This + // again is different from all other SfxUndoListener methods). + // So, we do not need to do this notification here ourself. } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper_Impl::actionUndone( const String& i_actionComment ) + void UndoManagerHelper_Impl::impl_addUndoAction( const Reference< XUndoAction >& i_action ) { - if ( bAPIActionRunning ) + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( ::osl::Thread::getCurrentIdentifier() == m_pRequestProcessor->getIdentifier(), + "UndoManagerHelper_Impl::impl_addUndoAction: expected to be executed serialized, in a dedicated thread!" ); + + IUndoManager& rUndoManager = getUndoManager(); + if ( !rUndoManager.IsUndoEnabled() ) + // ignore the request if the manager is locked return; - notify( i_actionComment, &XUndoManagerListener::actionUndone ); + const UndoManagerEvent aEventAdd( buildEvent( i_action->getTitle() ) ); + const EventObject aEventClear( getXUndoManager() ); + + const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::CurrentLevel ) > 0 ); + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.AddUndoAction( new UndoActionWrapper( i_action ) ); + } + const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::CurrentLevel ) > 0 ); + + aGuard.clear(); + // <--- SYNCHRONIZED + + m_aUndoListeners.notifyEach( &XUndoManagerListener::undoActionAdded, aEventAdd ); + if ( bHadRedoActions && !bHasRedoActions ) + m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared , aEventClear ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::impl_clear() + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( ::osl::Thread::getCurrentIdentifier() == m_pRequestProcessor->getIdentifier(), + "UndoManagerHelper_Impl::impl_clear: expected to be executed serialized, in a dedicated thread!" ); + + IUndoManager& rUndoManager = getUndoManager(); + if ( rUndoManager.IsInListAction() ) + throw UndoContextNotClosedException( ::rtl::OUString(), getXUndoManager() ); + + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.Clear(); + } + + const EventObject aEvent( getXUndoManager() ); + aGuard.clear(); + // <--- SYNCHRONIZED + + m_aUndoListeners.notifyEach( &XUndoManagerListener::allActionsCleared, aEvent ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::impl_clearRedo() + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( ::osl::Thread::getCurrentIdentifier() == m_pRequestProcessor->getIdentifier(), + "UndoManagerHelper_Impl::impl_clearRedo: expected to be executed serialized, in a dedicated thread!" ); + + IUndoManager& rUndoManager = getUndoManager(); + if ( rUndoManager.IsInListAction() ) + throw UndoContextNotClosedException( ::rtl::OUString(), getXUndoManager() ); + + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + rUndoManager.ClearRedo(); + } + + const EventObject aEvent( getXUndoManager() ); + aGuard.clear(); + // <--- SYNCHRONIZED + + m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEvent ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::impl_reset() + { + // SYNCHRONIZED ---> + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + OSL_PRECOND( ::osl::Thread::getCurrentIdentifier() == m_pRequestProcessor->getIdentifier(), + "UndoManagerHelper_Impl::impl_reset: expected to be executed serialized, in a dedicated thread!" ); + + IUndoManager& rUndoManager = getUndoManager(); + { + ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning ); + while ( rUndoManager.IsInListAction() ) + rUndoManager.LeaveListAction(); + rUndoManager.Clear(); + } + + const EventObject aEvent( getXUndoManager() ); + aGuard.clear(); + // <--- SYNCHRONIZED + + m_aUndoListeners.notifyEach( &XUndoManagerListener::resetAll, aEvent ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::processEvent( const ::comphelper::AnyEvent& i_event ) + { + UndoManagerRequest& rRequest( dynamic_cast< UndoManagerRequest& >( const_cast< ::comphelper::AnyEvent& >( i_event ) ) ); + rRequest.execute(); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper_Impl::actionUndone( const String& i_actionComment ) + { + UndoManagerEvent aEvent; + aEvent.Source = getXUndoManager(); + aEvent.UndoActionTitle = i_actionComment; + aEvent.UndoContextDepth = 0; // Undo can happen on level 0 only + m_aUndoListeners.notifyEach( &XUndoManagerListener::actionUndone, aEvent ); } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper_Impl::actionRedone( const String& i_actionComment ) { - if ( bAPIActionRunning ) - return; - - notify( i_actionComment, &XUndoManagerListener::actionRedone ); + UndoManagerEvent aEvent; + aEvent.Source = getXUndoManager(); + aEvent.UndoActionTitle = i_actionComment; + aEvent.UndoContextDepth = 0; // Redo can happen on level 0 only + m_aUndoListeners.notifyEach( &XUndoManagerListener::actionRedone, aEvent ); } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper_Impl::undoActionAdded( const String& i_actionComment ) { - if ( bAPIActionRunning ) + if ( m_bAPIActionRunning ) return; notify( i_actionComment, &XUndoManagerListener::undoActionAdded ); @@ -378,7 +779,7 @@ namespace framework //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper_Impl::cleared() { - if ( bAPIActionRunning ) + if ( m_bAPIActionRunning ) return; notify( &XUndoManagerListener::allActionsCleared ); @@ -387,7 +788,7 @@ namespace framework //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper_Impl::clearedRedo() { - if ( bAPIActionRunning ) + if ( m_bAPIActionRunning ) return; notify( &XUndoManagerListener::redoActionsCleared ); @@ -397,58 +798,58 @@ namespace framework void UndoManagerHelper_Impl::listActionEntered( const String& i_comment ) { #if OSL_DEBUG_LEVEL > 0 - aContextAPIFlags.push( bAPIActionRunning ); + m_aContextAPIFlags.push( m_bAPIActionRunning ); #endif - if ( bAPIActionRunning ) + if ( m_bAPIActionRunning ) return; notify( i_comment, &XUndoManagerListener::enteredContext ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper_Impl::listActionLeft() + void UndoManagerHelper_Impl::listActionLeft( const String& i_comment ) { #if OSL_DEBUG_LEVEL > 0 - const bool bCurrentContextIsAPIContext = aContextAPIFlags.top(); - aContextAPIFlags.pop(); - OSL_ENSURE( bCurrentContextIsAPIContext == bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" ); + const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top(); + m_aContextAPIFlags.pop(); + OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" ); #endif - if ( bAPIActionRunning ) + if ( m_bAPIActionRunning ) return; - notify( getUndoManager().GetUndoActionComment( 0, IUndoManager::CurrentLevel ), &XUndoManagerListener::leftContext ); + notify( i_comment, &XUndoManagerListener::leftContext ); } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper_Impl::listActionLeftAndMerged() { #if OSL_DEBUG_LEVEL > 0 - const bool bCurrentContextIsAPIContext = aContextAPIFlags.top(); - aContextAPIFlags.pop(); - OSL_ENSURE( bCurrentContextIsAPIContext == bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeftAndMerged: API and non-API contexts interwoven!" ); + const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top(); + m_aContextAPIFlags.pop(); + OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeftAndMerged: API and non-API contexts interwoven!" ); #endif - if ( bAPIActionRunning ) + if ( m_bAPIActionRunning ) return; - notify( ::rtl::OUString(), &XUndoManagerListener::leftHiddenContext ); + notify( &XUndoManagerListener::leftHiddenContext ); } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper_Impl::listActionCancelled() { #if OSL_DEBUG_LEVEL > 0 - const bool bCurrentContextIsAPIContext = aContextAPIFlags.top(); - aContextAPIFlags.pop(); - OSL_ENSURE( bCurrentContextIsAPIContext == bAPIActionRunning, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" ); + const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top(); + m_aContextAPIFlags.pop(); + OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" ); #endif - if ( bAPIActionRunning ) + if ( m_bAPIActionRunning ) return; - notify( ::rtl::OUString(), &XUndoManagerListener::cancelledContext ); + notify( &XUndoManagerListener::cancelledContext ); } //------------------------------------------------------------------------------------------------------------------ @@ -478,124 +879,91 @@ namespace framework } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::enterUndoContext( const ::rtl::OUString& i_title, IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper::enterUndoContext( const ::rtl::OUString& i_title, IMutexGuard& i_instanceLock ) { m_pImpl->enterUndoContext( i_title, false, i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::enterHiddenUndoContext( IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper::enterHiddenUndoContext( IMutexGuard& i_instanceLock ) { m_pImpl->enterUndoContext( ::rtl::OUString(), true, i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::leaveUndoContext( IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper::leaveUndoContext( IMutexGuard& i_instanceLock ) { - // SYNCHRONIZED ---> - IUndoManager& rUndoManager = m_pImpl->getUndoManager(); - if ( !rUndoManager.IsUndoEnabled() ) - // ignore this request if the manager is locked - return; - - if ( !rUndoManager.IsInListAction() ) - throw InvalidStateException( - ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "no active undo context" ) ), - m_pImpl->getXUndoManager() - ); - - USHORT nContextElements = 0; - bool isHiddenContext = false; - { - ::comphelper::FlagGuard aNotificationGuard( m_pImpl->bAPIActionRunning ); - - isHiddenContext = m_pImpl->aContextVisibilities.top(); - m_pImpl->aContextVisibilities.pop(); - if ( isHiddenContext ) - nContextElements = rUndoManager.LeaveAndMergeListAction(); - else - nContextElements = rUndoManager.LeaveListAction(); - } - - if ( nContextElements == 0 ) - m_pImpl->notify( ::rtl::OUString(), &XUndoManagerListener::cancelledContext, i_instanceLock ); - else if ( isHiddenContext ) - m_pImpl->notify( ::rtl::OUString(), &XUndoManagerListener::leftHiddenContext, i_instanceLock ); - else - m_pImpl->notify( rUndoManager.GetUndoActionComment( 0, IUndoManager::CurrentLevel ), &XUndoManagerListener::leftContext, i_instanceLock ); - // <--- SYNCHRONIZED + m_pImpl->leaveUndoContext( i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::addUndoAction( const Reference< XUndoAction >& i_action, IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper_Impl::undo( IMutexGuard& i_instanceLock ) { - // SYNCHRONIZED ---> - if ( !i_action.is() ) - throw IllegalArgumentException( - ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "illegal undo action object" ) ), - m_pImpl->getXUndoManager(), - 1 - ); - - IUndoManager& rUndoManager = m_pImpl->getUndoManager(); - if ( !rUndoManager.IsUndoEnabled() ) - // ignore the request if the manager is locked - return; - - const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::CurrentLevel ) > 0 ); - { - ::comphelper::FlagGuard aNotificationGuard( m_pImpl->bAPIActionRunning ); - rUndoManager.AddUndoAction( new UndoActionWrapper( i_action ) ); - } - const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::CurrentLevel ) > 0 ); - - m_pImpl->notify( i_action->getTitle(), &XUndoManagerListener::undoActionAdded, i_instanceLock ); - // <--- SYNCHRONIZED - - if ( bHadRedoActions && !bHasRedoActions ) - m_pImpl->notify( &XUndoManagerListener::redoActionsCleared ); + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_doUndoRedo, + this, + ::boost::ref( i_instanceLock ), + true + ), + i_instanceLock + ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::undo( IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper_Impl::redo( IMutexGuard& i_instanceLock ) { - m_pImpl->doUndoRedo( - &IUndoManager::GetUndoActionCount, - &IUndoManager::Undo, - &IUndoManager::GetUndoActionComment, - &XUndoManagerListener::actionUndone, + impl_processRequest( + ::boost::bind( + &UndoManagerHelper_Impl::impl_doUndoRedo, + this, + ::boost::ref( i_instanceLock ), + false + ), i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::redo( IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock ) { - m_pImpl->doUndoRedo( - &IUndoManager::GetRedoActionCount, - &IUndoManager::Redo, - &IUndoManager::GetRedoActionComment, - &XUndoManagerListener::actionRedone, - i_instanceLock - ); + m_pImpl->addUndoAction( i_action, i_instanceLock ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper::undo( IMutexGuard& i_instanceLock ) + { + m_pImpl->undo( i_instanceLock ); + } + + //------------------------------------------------------------------------------------------------------------------ + void UndoManagerHelper::redo( IMutexGuard& i_instanceLock ) + { + m_pImpl->redo( i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ ::sal_Bool UndoManagerHelper::isUndoPossible() const { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_pImpl->getMutex() ); IUndoManager& rUndoManager = m_pImpl->getUndoManager(); if ( rUndoManager.IsInListAction() ) return sal_False; return rUndoManager.GetUndoActionCount( IUndoManager::TopLevel ) > 0; + // <--- SYNCHRONIZED } //------------------------------------------------------------------------------------------------------------------ ::sal_Bool UndoManagerHelper::isRedoPossible() const { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_pImpl->getMutex() ); const IUndoManager& rUndoManager = m_pImpl->getUndoManager(); if ( rUndoManager.IsInListAction() ) return sal_False; return rUndoManager.GetRedoActionCount( IUndoManager::TopLevel ) > 0; + // <--- SYNCHRONIZED } //------------------------------------------------------------------------------------------------------------------ @@ -604,6 +972,9 @@ namespace framework //.............................................................................................................. ::rtl::OUString lcl_getCurrentActionTitle( UndoManagerHelper_Impl& i_impl, const bool i_undo ) { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( i_impl.getMutex() ); + const IUndoManager& rUndoManager = i_impl.getUndoManager(); const USHORT nActionCount = i_undo ? rUndoManager.GetUndoActionCount( IUndoManager::TopLevel ) @@ -617,11 +988,15 @@ namespace framework return i_undo ? rUndoManager.GetUndoActionComment( 0, IUndoManager::TopLevel ) : rUndoManager.GetRedoActionComment( 0, IUndoManager::TopLevel ); + // <--- SYNCHRONIZED } //.............................................................................................................. Sequence< ::rtl::OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl, const bool i_undo ) { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( i_impl.getMutex() ); + const IUndoManager& rUndoManager = i_impl.getUndoManager(); const USHORT nCount = i_undo ? rUndoManager.GetUndoActionCount( IUndoManager::TopLevel ) @@ -635,6 +1010,7 @@ namespace framework : rUndoManager.GetRedoActionComment( i, IUndoManager::TopLevel ); } return aTitles; + // <--- SYNCHRONIZED } } @@ -663,90 +1039,70 @@ namespace framework } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::clear( IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper::clear( IMutexGuard& i_instanceLock ) { - // SYNCHRONIZED ---> - IUndoManager& rUndoManager = m_pImpl->getUndoManager(); - if ( rUndoManager.IsInListAction() ) - throw UndoContextNotClosedException( ::rtl::OUString(), m_pImpl->getXUndoManager() ); - - { - ::comphelper::FlagGuard aNotificationGuard( m_pImpl->bAPIActionRunning ); - rUndoManager.Clear(); - } - - m_pImpl->notify( &XUndoManagerListener::allActionsCleared, i_instanceLock ); - // <--- SYNCHRONIZED + m_pImpl->clear( i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::clearRedo( IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper::clearRedo( IMutexGuard& i_instanceLock ) { - // SYNCHRONIZED ---> - IUndoManager& rUndoManager = m_pImpl->getUndoManager(); - if ( rUndoManager.IsInListAction() ) - throw UndoContextNotClosedException( ::rtl::OUString(), m_pImpl->getXUndoManager() ); - - { - ::comphelper::FlagGuard aNotificationGuard( m_pImpl->bAPIActionRunning ); - rUndoManager.ClearRedo(); - } - - m_pImpl->notify( &XUndoManagerListener::redoActionsCleared, i_instanceLock ); - // <--- SYNCHRONIZED + m_pImpl->clearRedo( i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ - void UndoManagerHelper::reset( IClearableInstanceLock& i_instanceLock ) + void UndoManagerHelper::reset( IMutexGuard& i_instanceLock ) { - // SYNCHRONIZED ---> - IUndoManager& rUndoManager = m_pImpl->getUndoManager(); - { - ::comphelper::FlagGuard aNotificationGuard( m_pImpl->bAPIActionRunning ); - while ( rUndoManager.IsInListAction() ) - rUndoManager.LeaveListAction(); - rUndoManager.Clear(); - } - - m_pImpl->notify( &XUndoManagerListener::resetAll, i_instanceLock ); - // <--- SYNCHRONIZED + m_pImpl->reset( i_instanceLock ); } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper::lock() { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_pImpl->getMutex() ); + IUndoManager& rUndoManager = m_pImpl->getUndoManager(); rUndoManager.EnableUndo( false ); + // <--- SYNCHRONIZED } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper::unlock() { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_pImpl->getMutex() ); + IUndoManager& rUndoManager = m_pImpl->getUndoManager(); if ( rUndoManager.IsUndoEnabled() ) throw NotLockedException( ::rtl::OUString::createFromAscii( "Undo manager is not locked" ), m_pImpl->getXUndoManager() ); rUndoManager.EnableUndo( true ); + // <--- SYNCHRONIZED } //------------------------------------------------------------------------------------------------------------------ ::sal_Bool UndoManagerHelper::isLocked() { + // SYNCHRONIZED ---> + ::osl::MutexGuard aGuard( m_pImpl->getMutex() ); + IUndoManager& rUndoManager = m_pImpl->getUndoManager(); return !rUndoManager.IsUndoEnabled(); + // <--- SYNCHRONIZED } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) { if ( i_listener.is() ) - m_pImpl->aUndoListeners.addInterface( i_listener ); + m_pImpl->addUndoManagerListener( i_listener ); } //------------------------------------------------------------------------------------------------------------------ void UndoManagerHelper::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) { if ( i_listener.is() ) - m_pImpl->aUndoListeners.removeInterface( i_listener ); + m_pImpl->removeUndoManagerListener( i_listener ); } //...................................................................................................................... diff --git a/framework/util/makefile.mk b/framework/util/makefile.mk index 13c3d70d0d3c..012077dbc7ab 100644 --- a/framework/util/makefile.mk +++ b/framework/util/makefile.mk @@ -424,6 +424,7 @@ $(MISC)$/$(SHL2TARGET).flt: makefile.mk @echo m_pLoader>$@ @echo _TI2>>$@ @echo _TI3>>$@ + @echo _TI8>>$@ @echo LIBMAIN>>$@ @echo LibMain>>$@ diff --git a/sfx2/inc/sfx2/sfxbasemodel.hxx b/sfx2/inc/sfx2/sfxbasemodel.hxx index 4367810e1675..2ba73f4034fc 100644 --- a/sfx2/inc/sfx2/sfxbasemodel.hxx +++ b/sfx2/inc/sfx2/sfxbasemodel.hxx @@ -1616,13 +1616,18 @@ public: { } + void reset() + { + m_aGuard.reset(); + } + void clear() { m_aGuard.clear(); } private: - ::vos::OClearableGuard m_aGuard; + ::osl::ResettableGuard< ::vos::IMutex > m_aGuard; }; #undef css diff --git a/sfx2/qa/complex/sfx2/UndoManager.java b/sfx2/qa/complex/sfx2/UndoManager.java index 421111e88f4e..c86ceef5a1ac 100755 --- a/sfx2/qa/complex/sfx2/UndoManager.java +++ b/sfx2/qa/complex/sfx2/UndoManager.java @@ -456,6 +456,7 @@ public class UndoManager public void leftHiddenContext( UndoManagerEvent i_event ) { assertFalse( "|leftHiddenContext| called after document was disposed", m_isDisposed ); + assertEquals( "|leftHiddenContext| is not expected to notify an action title", 0, i_event.UndoActionTitle.length() ); m_activeUndoContexts.pop(); assertEquals( "different opinions on the context nesting level (after leaving)", @@ -467,6 +468,7 @@ public class UndoManager public void cancelledContext( UndoManagerEvent i_event ) { assertFalse( "|cancelledContext| called after document was disposed", m_isDisposed ); + assertEquals( "|cancelledContext| is not expected to notify an action title", 0, i_event.UndoActionTitle.length() ); m_activeUndoContexts.pop(); assertEquals( "different opinions on the context nesting level (after cancelling)", @@ -590,6 +592,7 @@ public class UndoManager // close the document, ensure the Undo manager listener gets notified m_currentDocument.close(); + m_currentDocument = null; assertTrue( "document is closed, but the UndoManagerListener has not been notified of the disposal", m_undoListener.isDisposed() ); } diff --git a/sfx2/source/doc/docundomanager.cxx b/sfx2/source/doc/docundomanager.cxx index 39534bf9f90c..6fbe1996d85d 100755 --- a/sfx2/source/doc/docundomanager.cxx +++ b/sfx2/source/doc/docundomanager.cxx @@ -44,6 +44,7 @@ #include <tools/diagnose_ex.h> #include <framework/undomanagerhelper.hxx> +#include <boost/noncopyable.hpp> #include <stack> //...................................................................................................................... @@ -103,7 +104,6 @@ namespace sfx2 SfxObjectShell* getObjectShell() { return rAntiImpl.getBaseModel().GetObjectShell(); } // IUndoManagerImplementation - virtual ::osl::Mutex& getMutex(); virtual ::svl::IUndoManager& getImplUndoManager(); virtual Reference< XUndoManager > getThis(); @@ -131,12 +131,6 @@ namespace sfx2 }; //------------------------------------------------------------------------------------------------------------------ - ::osl::Mutex& DocumentUndoManager_Impl::getMutex() - { - return rAntiImpl.getMutex(); - } - - //------------------------------------------------------------------------------------------------------------------ ::svl::IUndoManager& DocumentUndoManager_Impl::getImplUndoManager() { ENSURE_OR_THROW( pUndoManager != NULL, "DocumentUndoManager_Impl::getImplUndoManager: no access to the doc's UndoManager implementation!" ); @@ -193,23 +187,65 @@ namespace sfx2 } //================================================================================================================== - //= SfxModelGuardFacade + //= SolarMutexFacade + //================================================================================================================== + /** a facade for the SolarMutex, implementing ::framework::IMutex (as opposed to ::vos::IMutex) + */ + class SolarMutexFacade : public ::framework::IMutex + { + public: + SolarMutexFacade() + { + } + + virtual void acquire() + { + Application::GetSolarMutex().acquire(); + } + + virtual void release() + { + Application::GetSolarMutex().release(); + } + }; + + //================================================================================================================== + //= UndoManagerGuard //================================================================================================================== - class SfxModelGuardFacade : public ::framework::IClearableInstanceLock + class UndoManagerGuard :public ::framework::IMutexGuard + ,public ::boost::noncopyable { public: - SfxModelGuardFacade( SfxModelGuard& i_guard ) - :m_guard( i_guard ) + UndoManagerGuard( DocumentUndoManager& i_undoManager ) + :m_guard( i_undoManager ) + ,m_solarMutexFacade() + { + } + + ~UndoManagerGuard() { } + virtual void reset() + { + m_guard.reset(); + } + virtual void clear() { m_guard.clear(); } + virtual ::framework::IMutex& getGuardedMutex() + { + // note that this means that we *know* that SfxModelGuard also locks the SolarMutex (nothing more, nothing less). + // If this ever changes, we need to adjust this code here, too. + return m_solarMutexFacade; + } + private: - SfxModelGuard& m_guard; + SfxModelGuard m_guard; + SolarMutexFacade m_solarMutexFacade; }; //================================================================================================================== @@ -248,16 +284,19 @@ namespace sfx2 //------------------------------------------------------------------------------------------------------------------ void SAL_CALL DocumentUndoManager::enterUndoContext( const ::rtl::OUString& i_title ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); - m_pImpl->aUndoHelper.enterUndoContext( i_title, SfxModelGuardFacade( aGuard ) ); + // SYNCHRONIZED ---> + UndoManagerGuard aGuard( *this ); + m_pImpl->aUndoHelper.enterUndoContext( i_title, aGuard ); + // <--- SYNCHRONIZED + m_pImpl->invalidateXDo_nolck(); } //------------------------------------------------------------------------------------------------------------------ void SAL_CALL DocumentUndoManager::enterHiddenUndoContext( ) throw (EmptyUndoStackException, RuntimeException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); - m_pImpl->aUndoHelper.enterHiddenUndoContext( SfxModelGuardFacade( aGuard ) ); + UndoManagerGuard aGuard( *this ); + m_pImpl->aUndoHelper.enterHiddenUndoContext( aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -266,8 +305,8 @@ namespace sfx2 void SAL_CALL DocumentUndoManager::leaveUndoContext( ) throw (InvalidStateException, RuntimeException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); - m_pImpl->aUndoHelper.leaveUndoContext( SfxModelGuardFacade( aGuard ) ); + UndoManagerGuard aGuard( *this ); + m_pImpl->aUndoHelper.leaveUndoContext( aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -276,8 +315,8 @@ namespace sfx2 void SAL_CALL DocumentUndoManager::addUndoAction( const Reference< XUndoAction >& i_action ) throw (RuntimeException, IllegalArgumentException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); - m_pImpl->aUndoHelper.addUndoAction( i_action, SfxModelGuardFacade( aGuard ) ); + UndoManagerGuard aGuard( *this ); + m_pImpl->aUndoHelper.addUndoAction( i_action, aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -286,9 +325,9 @@ namespace sfx2 void SAL_CALL DocumentUndoManager::undo( ) throw (EmptyUndoStackException, UndoContextNotClosedException, UndoFailedException, RuntimeException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); m_pImpl->enterViewStandardMode(); - m_pImpl->aUndoHelper.undo( SfxModelGuardFacade( aGuard ) ); + m_pImpl->aUndoHelper.undo( aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -297,9 +336,9 @@ namespace sfx2 void SAL_CALL DocumentUndoManager::redo( ) throw (EmptyUndoStackException, UndoContextNotClosedException, UndoFailedException, RuntimeException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); m_pImpl->enterViewStandardMode(); - m_pImpl->aUndoHelper.redo( SfxModelGuardFacade( aGuard ) ); + m_pImpl->aUndoHelper.redo( aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -307,42 +346,42 @@ namespace sfx2 //------------------------------------------------------------------------------------------------------------------ ::sal_Bool SAL_CALL DocumentUndoManager::isUndoPossible( ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.isUndoPossible(); } //------------------------------------------------------------------------------------------------------------------ ::sal_Bool SAL_CALL DocumentUndoManager::isRedoPossible( ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.isRedoPossible(); } //------------------------------------------------------------------------------------------------------------------ ::rtl::OUString SAL_CALL DocumentUndoManager::getCurrentUndoActionTitle( ) throw (EmptyUndoStackException, RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.getCurrentUndoActionTitle(); } //------------------------------------------------------------------------------------------------------------------ ::rtl::OUString SAL_CALL DocumentUndoManager::getCurrentRedoActionTitle( ) throw (EmptyUndoStackException, RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.getCurrentRedoActionTitle(); } //------------------------------------------------------------------------------------------------------------------ Sequence< ::rtl::OUString > SAL_CALL DocumentUndoManager::getAllUndoActionTitles( ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.getAllUndoActionTitles(); } //------------------------------------------------------------------------------------------------------------------ Sequence< ::rtl::OUString > SAL_CALL DocumentUndoManager::getAllRedoActionTitles( ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.getAllRedoActionTitles(); } @@ -350,8 +389,8 @@ namespace sfx2 void SAL_CALL DocumentUndoManager::clear( ) throw (UndoContextNotClosedException, RuntimeException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); - m_pImpl->aUndoHelper.clear( SfxModelGuardFacade( aGuard ) ); + UndoManagerGuard aGuard( *this ); + m_pImpl->aUndoHelper.clear( aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -360,8 +399,8 @@ namespace sfx2 void SAL_CALL DocumentUndoManager::clearRedo( ) throw (UndoContextNotClosedException, RuntimeException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); - m_pImpl->aUndoHelper.clearRedo( SfxModelGuardFacade( aGuard ) ); + UndoManagerGuard aGuard( *this ); + m_pImpl->aUndoHelper.clearRedo( aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -370,8 +409,8 @@ namespace sfx2 void SAL_CALL DocumentUndoManager::reset() throw (RuntimeException) { // SYNCHRONIZED ---> - SfxModelGuard aGuard( *this ); - m_pImpl->aUndoHelper.reset( SfxModelGuardFacade( aGuard ) ); + UndoManagerGuard aGuard( *this ); + m_pImpl->aUndoHelper.reset( aGuard ); // <--- SYNCHRONIZED m_pImpl->invalidateXDo_nolck(); } @@ -379,35 +418,35 @@ namespace sfx2 //------------------------------------------------------------------------------------------------------------------ void SAL_CALL DocumentUndoManager::lock( ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); m_pImpl->aUndoHelper.lock(); } //------------------------------------------------------------------------------------------------------------------ void SAL_CALL DocumentUndoManager::unlock( ) throw (RuntimeException, NotLockedException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); m_pImpl->aUndoHelper.unlock(); } //------------------------------------------------------------------------------------------------------------------ ::sal_Bool SAL_CALL DocumentUndoManager::isLocked( ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.isLocked(); } //------------------------------------------------------------------------------------------------------------------ void SAL_CALL DocumentUndoManager::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.addUndoManagerListener( i_listener ); } //------------------------------------------------------------------------------------------------------------------ void SAL_CALL DocumentUndoManager::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) throw (RuntimeException) { - SfxModelGuard aGuard( *this ); + UndoManagerGuard aGuard( *this ); return m_pImpl->aUndoHelper.removeUndoManagerListener( i_listener ); } |