summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--svl/inc/svl/undo.hxx23
-rw-r--r--svl/source/undo/undo.cxx525
2 files changed, 392 insertions, 156 deletions
diff --git a/svl/inc/svl/undo.hxx b/svl/inc/svl/undo.hxx
index 424900c79542..da8ec3b4268f 100644
--- a/svl/inc/svl/undo.hxx
+++ b/svl/inc/svl/undo.hxx
@@ -139,9 +139,9 @@ class SVL_DLLPUBLIC SfxListUndoAction : public SfxUndoAction, public SfxUndoArra
class SAL_NO_VTABLE SfxUndoListener
{
public:
- virtual void actionUndone( SfxUndoAction& i_action ) = 0;
- virtual void actionRedone( SfxUndoAction& i_action ) = 0;
- virtual void undoActionAdded( SfxUndoAction& i_action ) = 0;
+ virtual void actionUndone( const String& i_actionComment ) = 0;
+ virtual void actionRedone( const String& i_actionComment ) = 0;
+ virtual void undoActionAdded( const String& i_actionComment ) = 0;
virtual void cleared() = 0;
virtual void clearedRedo() = 0;
virtual void listActionEntered( const String& i_comment ) = 0;
@@ -246,6 +246,12 @@ namespace svl
//=========================================================================
+namespace svl { namespace undo { namespace impl
+{
+ class UndoManagerGuard;
+ class LockGuard;
+} } }
+
struct SfxUndoManager_Data;
class SVL_DLLPUBLIC SfxUndoManager : public ::svl::IUndoManager
{
@@ -288,7 +294,16 @@ public:
virtual void RemoveUndoListener( SfxUndoListener& i_listener );
private:
- USHORT ImplLeaveListAction( const bool i_merge );
+ USHORT ImplLeaveListAction( const bool i_merge );
+ bool ImplAddUndoAction_NoNotify( SfxUndoAction* pAction, BOOL bTryMerge, ::svl::undo::impl::UndoManagerGuard& i_guard );
+ void ImplClearRedo( ::svl::undo::impl::UndoManagerGuard& i_guard );
+ void ImplClearUndo( ::svl::undo::impl::UndoManagerGuard& i_guard );
+ USHORT ImplGetRedoActionCount_Lock( bool const i_currentLevel = CurrentLevel ) const;
+ bool ImplIsUndoEnabled_Lock() const;
+ bool ImplIsInListAction_Lock() const;
+ void ImplEnableUndo_Lock( bool const i_enable );
+
+ friend ::svl::undo::impl::LockGuard;
};
//=========================================================================
diff --git a/svl/source/undo/undo.cxx b/svl/source/undo/undo.cxx
index 5c0a4a1d816c..e401c4843258 100644
--- a/svl/source/undo/undo.cxx
+++ b/svl/source/undo/undo.cxx
@@ -37,6 +37,7 @@
#include <svl/undo.hxx>
#include <vector>
+#include <list>
using ::com::sun::star::uno::Exception;
@@ -156,8 +157,9 @@ BOOL SfxUndoAction::CanRepeat(SfxRepeatTarget&) const
typedef ::std::vector< SfxUndoListener* > UndoListeners;
-struct SfxUndoManager_Data
+struct SVL_DLLPRIVATE SfxUndoManager_Data
{
+ ::osl::Mutex aMutex;
SfxUndoArray* pUndoArray;
SfxUndoArray* pActUndoArray;
SfxUndoArray* pFatherUndoArray;
@@ -186,42 +188,172 @@ struct SfxUndoManager_Data
//========================================================================
-namespace
+namespace svl { namespace undo { namespace impl
{
//--------------------------------------------------------------------
- struct LockGuard
+ class SVL_DLLPRIVATE LockGuard
{
- LockGuard( ::svl::IUndoManager& i_manager )
+ public:
+ LockGuard( SfxUndoManager& i_manager )
:m_manager( i_manager )
{
- m_manager.EnableUndo( false );
+ m_manager.ImplEnableUndo_Lock( false );
}
~LockGuard()
{
- m_manager.EnableUndo( true );
+ m_manager.ImplEnableUndo_Lock( true );
}
private:
- ::svl::IUndoManager& m_manager;
+ SfxUndoManager& m_manager;
};
//--------------------------------------------------------------------
- struct NotifyUndoListener : public ::std::unary_function< SfxUndoListener*, void >
+ typedef void ( SfxUndoListener::*UndoListenerVoidMethod )();
+ typedef void ( SfxUndoListener::*UndoListenerStringMethod )( const String& );
+
+ //--------------------------------------------------------------------
+ struct SVL_DLLPRIVATE NotifyUndoListener : public ::std::unary_function< SfxUndoListener*, void >
{
- NotifyUndoListener( void ( SfxUndoListener::*i_notificationMethod )() )
+ NotifyUndoListener()
+ :m_notificationMethod( NULL )
+ ,m_altNotificationMethod( NULL )
+ ,m_sActionComment()
+ {
+ }
+
+ NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
:m_notificationMethod( i_notificationMethod )
+ ,m_altNotificationMethod( NULL )
+ ,m_sActionComment()
+ {
+ }
+
+ NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const String& i_actionComment )
+ :m_notificationMethod( NULL )
+ ,m_altNotificationMethod( i_notificationMethod )
+ ,m_sActionComment( i_actionComment )
+ {
+ }
+
+ bool is() const
{
+ return ( m_notificationMethod != NULL ) || ( m_altNotificationMethod != NULL );
}
void operator()( SfxUndoListener* i_listener ) const
{
- ( i_listener->*m_notificationMethod )();
+ OSL_PRECOND( is(), "NotifyUndoListener: this will crash!" );
+ if ( m_altNotificationMethod != NULL )
+ {
+ ( i_listener->*m_altNotificationMethod )( m_sActionComment );
+ }
+ else
+ {
+ ( i_listener->*m_notificationMethod )();
+ }
}
- void ( SfxUndoListener::*m_notificationMethod )();
+ private:
+ UndoListenerVoidMethod m_notificationMethod;
+ UndoListenerStringMethod m_altNotificationMethod;
+ String m_sActionComment;
};
-}
+
+ //--------------------------------------------------------------------
+ class SVL_DLLPRIVATE UndoManagerGuard
+ {
+ public:
+ UndoManagerGuard( SfxUndoManager_Data& i_managerData )
+ :m_rManagerData( i_managerData )
+ ,m_aGuard( i_managerData.aMutex )
+ ,m_notifiers()
+ {
+ }
+
+ ~UndoManagerGuard();
+
+ void clear()
+ {
+ m_aGuard.clear();
+ }
+
+ void reset()
+ {
+ m_aGuard.reset();
+ }
+
+ /** marks the given Undo action for deletion
+
+ The Undo action will be put into a list, whose members will be deleted from within the destructor of the
+ UndoManagerGuard. This deletion will happen without the UndoManager's mutex locked.
+ */
+ void markForDeletion( SfxUndoAction* i_action )
+ {
+ // remember
+ if ( i_action )
+ m_aUndoActionsCleanup.push_back( i_action );
+ }
+
+ /** schedules the given SfxUndoListener method to be called for all registered listeners.
+
+ The notification will happen after the Undo manager's mutex has been released, and after all pending
+ deletions of Undo actions are done.
+ */
+ void scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
+ {
+ m_notifiers.push_back( NotifyUndoListener( i_notificationMethod ) );
+ }
+
+ void scheduleNotification( UndoListenerStringMethod i_notificationMethod, const String& i_actionComment )
+ {
+ m_notifiers.push_back( NotifyUndoListener( i_notificationMethod, i_actionComment ) );
+ }
+
+ private:
+ SfxUndoManager_Data& m_rManagerData;
+ ::osl::ResettableMutexGuard m_aGuard;
+ ::std::list< SfxUndoAction* > m_aUndoActionsCleanup;
+ ::std::list< NotifyUndoListener > m_notifiers;
+ };
+
+ UndoManagerGuard::~UndoManagerGuard()
+ {
+ // copy members
+ UndoListeners aListenersCopy( m_rManagerData.aListeners );
+
+ // release mutex
+ m_aGuard.clear();
+
+ // delete all actions
+ while ( !m_aUndoActionsCleanup.empty() )
+ {
+ SfxUndoAction* pAction = m_aUndoActionsCleanup.front();
+ m_aUndoActionsCleanup.pop_front();
+ try
+ {
+ delete pAction;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION();
+ }
+ }
+
+ // handle scheduled notification
+ for ( ::std::list< NotifyUndoListener >::const_iterator notifier = m_notifiers.begin();
+ notifier != m_notifiers.end();
+ ++notifier
+ )
+ {
+ if ( notifier->is() )
+ ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), *notifier );
+ }
+ }
+} } }
+
+using namespace ::svl::undo::impl;
//========================================================================
@@ -234,20 +366,34 @@ SfxUndoManager::SfxUndoManager( USHORT nMaxUndoActionCount )
SfxUndoManager::~SfxUndoManager()
{
- ::std::for_each( m_pData->aListeners.begin(), m_pData->aListeners.end(),
+ UndoListeners aListenersCopy;
+ {
+ UndoManagerGuard aGuard( *m_pData );
+ aListenersCopy = m_pData->aListeners;
+ }
+
+ ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(),
NotifyUndoListener( &SfxUndoListener::undoManagerDying ) );
}
//------------------------------------------------------------------------
-void SfxUndoManager::EnableUndo( bool bEnable )
+void SfxUndoManager::EnableUndo( bool i_enable )
{
- DBG_TESTSOLARMUTEX();
- if ( !bEnable )
+ UndoManagerGuard aGuard( *m_pData );
+ ImplEnableUndo_Lock( i_enable );
+
+}
+
+//------------------------------------------------------------------------
+
+void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
+{
+ if ( !i_enable )
++m_pData->mnLockCount;
else
{
- OSL_PRECOND( m_pData->mnLockCount > 0, "SfxUndoManager::EnableUndo: not disabled, so why enabling?" );
+ OSL_PRECOND( m_pData->mnLockCount > 0, "SfxUndoManager::ImplEnableUndo_NoNotify: not disabled, so why enabling?" );
if ( m_pData->mnLockCount > 0 )
--m_pData->mnLockCount;
}
@@ -257,7 +403,14 @@ void SfxUndoManager::EnableUndo( bool bEnable )
bool SfxUndoManager::IsUndoEnabled() const
{
- DBG_TESTSOLARMUTEX();
+ UndoManagerGuard aGuard( *m_pData );
+ return ImplIsUndoEnabled_Lock();
+}
+
+//------------------------------------------------------------------------
+
+bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
+{
return m_pData->mnLockCount == 0;
}
@@ -265,6 +418,8 @@ bool SfxUndoManager::IsUndoEnabled() const
void SfxUndoManager::SetMaxUndoActionCount( USHORT nMaxUndoActionCount )
{
+ UndoManagerGuard aGuard( *m_pData );
+
// Remove entries from the pActUndoArray when we have to reduce
// the number of entries due to a lower nMaxUndoActionCount.
// Both redo and undo action entries will be removed until we reached the
@@ -280,7 +435,7 @@ void SfxUndoManager::SetMaxUndoActionCount( USHORT nMaxUndoActionCount )
{
if ( !m_pData->pActUndoArray->aUndoActions[nPos-1]->IsLinked() )
{
- delete m_pData->pActUndoArray->aUndoActions[nPos-1];
+ aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[ nPos-1 ] );
m_pData->pActUndoArray->aUndoActions.Remove( nPos-1 );
--nNumToDelete;
}
@@ -290,7 +445,7 @@ void SfxUndoManager::SetMaxUndoActionCount( USHORT nMaxUndoActionCount )
{
if ( !m_pData->pActUndoArray->aUndoActions[0]->IsLinked() )
{
- delete m_pData->pActUndoArray->aUndoActions[0];
+ aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[0] );
m_pData->pActUndoArray->aUndoActions.Remove(0);
--m_pData->pActUndoArray->nCurUndoAction;
--nNumToDelete;
@@ -309,6 +464,7 @@ void SfxUndoManager::SetMaxUndoActionCount( USHORT nMaxUndoActionCount )
USHORT SfxUndoManager::GetMaxUndoActionCount() const
{
+ UndoManagerGuard aGuard( *m_pData );
return m_pData->pActUndoArray->nMaxUndoActions;
}
@@ -316,91 +472,121 @@ USHORT SfxUndoManager::GetMaxUndoActionCount() const
void SfxUndoManager::Clear()
{
+ UndoManagerGuard aGuard( *m_pData );
+
+ // clear array
while ( m_pData->pActUndoArray->aUndoActions.Count() )
{
- SfxUndoAction *pAction=
- m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->aUndoActions.Count() - 1];
+ aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.Count() - 1 ] );
m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->aUndoActions.Count() - 1 );
- delete pAction;
}
m_pData->pActUndoArray->nCurUndoAction = 0;
// notify listeners
- ::std::for_each( m_pData->aListeners.begin(), m_pData->aListeners.end(),
- NotifyUndoListener( &SfxUndoListener::cleared ) );
+ aGuard.scheduleNotification( &SfxUndoListener::cleared );
}
//------------------------------------------------------------------------
void SfxUndoManager::ClearRedo()
{
+ UndoManagerGuard aGuard( *m_pData );
+ ImplClearRedo( aGuard );
+}
+
+//------------------------------------------------------------------------
+
+void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
+{
+ while ( m_pData->pActUndoArray->nCurUndoAction > 0 )
+ {
+ SfxUndoAction* pUndoAction = m_pData->pActUndoArray->aUndoActions[0];
+ m_pData->pActUndoArray->aUndoActions.Remove( 0 );
+ i_guard.markForDeletion( pUndoAction );
+ --m_pData->pActUndoArray->nCurUndoAction;
+ }
+ // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
+}
+
+//------------------------------------------------------------------------
+
+void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard )
+{
+ // clearance
while ( m_pData->pActUndoArray->aUndoActions.Count() > m_pData->pActUndoArray->nCurUndoAction )
{
- SfxUndoAction *pAction=
+ SfxUndoAction *pAction =
m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->aUndoActions.Count() - 1];
m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->aUndoActions.Count() - 1 );
- delete pAction;
+ i_guard.markForDeletion( pAction );
}
- ::std::for_each( m_pData->aListeners.begin(), m_pData->aListeners.end(),
- NotifyUndoListener( &SfxUndoListener::clearedRedo ) );
+ // notification
+ i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
}
//------------------------------------------------------------------------
-void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, BOOL bTryMerge )
+bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction *pAction, BOOL bTryMerge, UndoManagerGuard& i_guard )
{
- if ( IsUndoEnabled() )
+ if ( !ImplIsUndoEnabled_Lock() || ( m_pData->pActUndoArray->nMaxUndoActions == 0 ) )
{
- // Redo-Actions loeschen
- if ( GetRedoActionCount() > 0 )
- ClearRedo();
+ i_guard.markForDeletion( pAction );
+ return false;
+ }
- if ( m_pData->pActUndoArray->nMaxUndoActions )
- {
- SfxUndoAction *pTmpAction = m_pData->pActUndoArray->nCurUndoAction ?
- m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1] : 0;
+ // Redo-Actions loeschen
+ if ( ImplGetRedoActionCount_Lock() > 0 )
+ ImplClearRedo( i_guard );
- if ( !bTryMerge || !(pTmpAction && pTmpAction->Merge(pAction)) )
- {
- // respect max number
- if( m_pData->pActUndoArray == m_pData->pUndoArray )
- while( m_pData->pActUndoArray->aUndoActions.Count() >=
- m_pData->pActUndoArray->nMaxUndoActions &&
- !m_pData->pActUndoArray->aUndoActions[0]->IsLinked() )
- {
- delete m_pData->pActUndoArray->aUndoActions[0];
- m_pData->pActUndoArray->aUndoActions.Remove(0);
- --m_pData->pActUndoArray->nCurUndoAction;
- }
-
- // append new action
- const SfxUndoAction* pTemp = pAction;
- m_pData->pActUndoArray->aUndoActions.Insert(
- pTemp, m_pData->pActUndoArray->nCurUndoAction++ );
-
- // notify listeners
- for ( UndoListeners::const_iterator listener = m_pData->aListeners.begin();
- listener != m_pData->aListeners.end();
- ++listener
- )
- {
- (*listener)->undoActionAdded( *pAction );
- }
+ // merge, if required
+ SfxUndoAction* pMergeWithAction = m_pData->pActUndoArray->nCurUndoAction ?
+ m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1] : NULL;
+ if ( bTryMerge && ( !pMergeWithAction || !pMergeWithAction->Merge( pAction ) ) )
+ {
+ i_guard.markForDeletion( pAction );
+ return false;
+ }
- // outta here
- return;
- }
+ // respect max number
+ if( m_pData->pActUndoArray == m_pData->pUndoArray )
+ {
+ while( m_pData->pActUndoArray->aUndoActions.Count() >=
+ m_pData->pActUndoArray->nMaxUndoActions &&
+ !m_pData->pActUndoArray->aUndoActions[0]->IsLinked() )
+ {
+ i_guard.markForDeletion( m_pData->pActUndoArray->aUndoActions[0] );
+ m_pData->pActUndoArray->aUndoActions.Remove(0);
+ --m_pData->pActUndoArray->nCurUndoAction;
}
}
- delete pAction;
+
+ // append new action
+ const SfxUndoAction* pMakeCompilerHappy = pAction;
+ m_pData->pActUndoArray->aUndoActions.Insert( pMakeCompilerHappy, m_pData->pActUndoArray->nCurUndoAction++ );
+ return true;
+}
+
+//------------------------------------------------------------------------
+
+void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, BOOL bTryMerge )
+{
+ UndoManagerGuard aGuard( *m_pData );
+
+ // add
+ if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, aGuard ) )
+ {
+ // notify listeners
+ aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
+ }
}
//------------------------------------------------------------------------
USHORT SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
{
+ UndoManagerGuard aGuard( *m_pData );
const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
return pUndoArray->nCurUndoAction;
}
@@ -409,9 +595,10 @@ USHORT SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
XubString SfxUndoManager::GetUndoActionComment( USHORT nNo, bool const i_currentLevel ) const
{
- const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
+ UndoManagerGuard aGuard( *m_pData );
String sComment;
+ const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
DBG_ASSERT( nNo < pUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" );
if( nNo < pUndoArray->nCurUndoAction )
{
@@ -424,30 +611,24 @@ XubString SfxUndoManager::GetUndoActionComment( USHORT nNo, bool const i_current
USHORT SfxUndoManager::GetUndoActionId() const
{
+ UndoManagerGuard aGuard( *m_pData );
+
DBG_ASSERT( m_pData->pActUndoArray->nCurUndoAction > 0, "svl::SfxUndoManager::GetUndoActionId(), illegal id!" );
- if( m_pData->pActUndoArray->nCurUndoAction > 0 )
- {
- return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1]->GetId();
- }
- else
- {
- return 0;
- }
+ if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
+ return NULL;
+ return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1]->GetId();
}
//------------------------------------------------------------------------
SfxUndoAction* SfxUndoManager::GetUndoAction( USHORT nNo ) const
{
+ UndoManagerGuard aGuard( *m_pData );
+
DBG_ASSERT( nNo < m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
- if( nNo < m_pData->pActUndoArray->nCurUndoAction )
- {
- return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1-nNo]; //!
- }
- else
- {
- return 0;
- }
+ if( nNo >= m_pData->pActUndoArray->nCurUndoAction )
+ return NULL;
+ return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1-nNo]; //!
}
//------------------------------------------------------------------------
@@ -455,27 +636,29 @@ SfxUndoAction* SfxUndoManager::GetUndoAction( USHORT nNo ) const
/** clears the redo stack and removes the top undo action */
void SfxUndoManager::RemoveLastUndoAction()
{
- DBG_ASSERT( m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
- if( m_pData->pActUndoArray->nCurUndoAction )
- {
- m_pData->pActUndoArray->nCurUndoAction--;
+ UndoManagerGuard aGuard( *m_pData );
+
+ ENSURE_OR_RETURN_VOID( m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
- // delete redo-actions and top action
- USHORT nPos;
- for ( nPos = m_pData->pActUndoArray->aUndoActions.Count(); nPos > m_pData->pActUndoArray->nCurUndoAction; --nPos )
- delete m_pData->pActUndoArray->aUndoActions[nPos-1];
+ m_pData->pActUndoArray->nCurUndoAction--;
- m_pData->pActUndoArray->aUndoActions.Remove(
- m_pData->pActUndoArray->nCurUndoAction,
- m_pData->pActUndoArray->aUndoActions.Count() - m_pData->pActUndoArray->nCurUndoAction );
+ // delete redo-actions and top action
+ USHORT nPos;
+ for ( nPos = m_pData->pActUndoArray->aUndoActions.Count(); nPos > m_pData->pActUndoArray->nCurUndoAction; --nPos )
+ {
+ aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[nPos-1] );
}
+
+ m_pData->pActUndoArray->aUndoActions.Remove(
+ m_pData->pActUndoArray->nCurUndoAction,
+ m_pData->pActUndoArray->aUndoActions.Count() - m_pData->pActUndoArray->nCurUndoAction );
}
//------------------------------------------------------------------------
bool SfxUndoManager::IsDoing() const
{
- DBG_TESTSOLARMUTEX();
+ UndoManagerGuard aGuard( *m_pData );
return m_pData->mbDoing;
}
@@ -483,13 +666,13 @@ bool SfxUndoManager::IsDoing() const
BOOL SfxUndoManager::Undo()
{
- DBG_TESTSOLARMUTEX();
+ UndoManagerGuard aGuard( *m_pData );
OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
- ::comphelper::FlagGuard aGuard( m_pData->mbDoing );
+ ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
LockGuard aLockGuard( *this );
- if ( IsInListAction() )
+ if ( ImplIsInListAction_Lock() )
{
OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" );
return FALSE;
@@ -501,33 +684,38 @@ BOOL SfxUndoManager::Undo()
return FALSE;
}
- SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 1 ];
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ];
+ const String sActionComment = pAction->GetComment();
try
{
+ // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
+ // nowadays ...
+ aGuard.clear();
pAction->Undo();
+ aGuard.reset();
}
- catch( const Exception& )
+ catch( ... )
{
- // assume that this is a permanent failure, and clear the Undo stack.
- while ( m_pData->pActUndoArray->nCurUndoAction > 0 )
+ aGuard.reset();
+
+ // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
+ // we still find pAction in our current Undo array
+ USHORT nCurAction = 0;
+ while ( nCurAction < m_pData->pActUndoArray->aUndoActions.Count() )
{
- SfxUndoAction* pUndoAction = m_pData->pActUndoArray->aUndoActions[0];
- m_pData->pActUndoArray->aUndoActions.Remove( 0 );
- delete pUndoAction;
- --m_pData->pActUndoArray->nCurUndoAction;
+ if ( m_pData->pActUndoArray->aUndoActions[ nCurAction++ ] == pAction )
+ {
+ // the Undo action is still there ...
+ // assume the error is a permanent failure, and clear the Undo stack
+ ImplClearUndo( aGuard );
+ throw;
+ }
}
- // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
+ OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
throw;
}
- --m_pData->pActUndoArray->nCurUndoAction;
- for ( UndoListeners::const_iterator listener = m_pData->aListeners.begin();
- listener != m_pData->aListeners.end();
- ++listener
- )
- {
- (*listener)->actionUndone( *pAction );
- }
+ aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
return TRUE;
}
@@ -536,6 +724,14 @@ BOOL SfxUndoManager::Undo()
USHORT SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
{
+ UndoManagerGuard aGuard( *m_pData );
+ return ImplGetRedoActionCount_Lock( i_currentLevel );
+}
+
+//------------------------------------------------------------------------
+
+USHORT SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
+{
const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
return pUndoArray->aUndoActions.Count() - pUndoArray->nCurUndoAction;
}
@@ -544,6 +740,7 @@ USHORT SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
XubString SfxUndoManager::GetRedoActionComment( USHORT nNo, bool const i_currentLevel ) const
{
+ UndoManagerGuard aGuard( *m_pData );
const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ]->GetComment();
}
@@ -552,13 +749,13 @@ XubString SfxUndoManager::GetRedoActionComment( USHORT nNo, bool const i_current
BOOL SfxUndoManager::Redo()
{
- DBG_TESTSOLARMUTEX();
+ UndoManagerGuard aGuard( *m_pData );
OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
- ::comphelper::FlagGuard aGuard( m_pData->mbDoing );
+ ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
LockGuard aLockGuard( *this );
- if ( IsInListAction() )
+ if ( ImplIsInListAction_Lock() )
{
OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
return FALSE;
@@ -570,25 +767,39 @@ BOOL SfxUndoManager::Redo()
return FALSE;
}
- SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction];
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ];
+ const String sActionComment = pAction->GetComment();
try
{
+ // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
+ // nowadays ...
+ aGuard.clear();
pAction->Redo();
+ aGuard.reset();
}
- catch( const Exception& )
+ catch( ... )
{
- ClearRedo();
+ aGuard.reset();
+
+ // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
+ // we still find pAction in our current Undo array
+ USHORT nCurAction = 0;
+ while ( nCurAction < m_pData->pActUndoArray->aUndoActions.Count() )
+ {
+ if ( m_pData->pActUndoArray->aUndoActions[ nCurAction ] == pAction )
+ {
+ // the Undo action is still there ...
+ // assume the error is a permanent failure, and clear the Undo stack
+ ImplClearRedo( aGuard );
+ throw;
+ }
+ ++nCurAction;
+ }
+ OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
throw;
}
- ++m_pData->pActUndoArray->nCurUndoAction;
- for ( UndoListeners::const_iterator listener = m_pData->aListeners.begin();
- listener != m_pData->aListeners.end();
- ++listener
- )
- {
- (*listener)->actionRedone( *pAction );
- }
+ aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
return TRUE;
}
@@ -597,6 +808,7 @@ BOOL SfxUndoManager::Redo()
USHORT SfxUndoManager::GetRepeatActionCount() const
{
+ UndoManagerGuard aGuard( *m_pData );
return m_pData->pActUndoArray->aUndoActions.Count();
}
@@ -604,6 +816,7 @@ USHORT SfxUndoManager::GetRepeatActionCount() const
XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) const
{
+ UndoManagerGuard aGuard( *m_pData );
return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.Count() - 1 ]
->GetRepeatComment(rTarget);
}
@@ -612,9 +825,11 @@ XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) cons
BOOL SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
{
+ UndoManagerGuard aGuard( *m_pData );
if ( m_pData->pActUndoArray->aUndoActions.Count() )
{
SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.Count() - 1 ];
+ aGuard.clear();
if ( pAction->CanRepeat( rTarget ) )
pAction->Repeat( rTarget );
return TRUE;
@@ -627,12 +842,12 @@ BOOL SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
BOOL SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
{
+ UndoManagerGuard aGuard( *m_pData );
if ( m_pData->pActUndoArray->aUndoActions.Count() > 0 )
{
USHORT nActionNo = m_pData->pActUndoArray->aUndoActions.Count() - 1;
return m_pData->pActUndoArray->aUndoActions[nActionNo]->CanRepeat(rTarget);
}
-
return FALSE;
}
@@ -640,7 +855,7 @@ BOOL SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
{
- DBG_TESTSOLARMUTEX();
+ UndoManagerGuard aGuard( *m_pData );
m_pData->aListeners.push_back( &i_listener );
}
@@ -648,7 +863,7 @@ void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
{
- DBG_TESTSOLARMUTEX();
+ UndoManagerGuard aGuard( *m_pData );
for ( UndoListeners::iterator lookup = m_pData->aListeners.begin();
lookup != m_pData->aListeners.end();
++lookup
@@ -673,31 +888,35 @@ void SfxUndoManager::EnterListAction(
*/
{
- if( !IsUndoEnabled() )
+ UndoManagerGuard aGuard( *m_pData );
+
+ if( !ImplIsUndoEnabled_Lock() )
return;
if ( !m_pData->pUndoArray->nMaxUndoActions )
return;
m_pData->pFatherUndoArray = m_pData->pActUndoArray;
- SfxListUndoAction *pAction=new SfxListUndoAction(
- rComment, rRepeatComment, nId, m_pData->pActUndoArray);
- AddUndoAction( pAction );
+ SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
+ ImplAddUndoAction_NoNotify( pAction, FALSE, aGuard );
m_pData->pActUndoArray=pAction;
- for ( UndoListeners::const_iterator listener = m_pData->aListeners.begin();
- listener != m_pData->aListeners.end();
- ++listener
- )
- {
- (*listener)->listActionEntered( rComment );
- }
+ // notification
+ aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
}
//------------------------------------------------------------------------
bool SfxUndoManager::IsInListAction() const
{
+ UndoManagerGuard aGuard( *m_pData );
+ return ImplIsInListAction_Lock();
+}
+
+//------------------------------------------------------------------------
+
+bool SfxUndoManager::ImplIsInListAction_Lock() const
+{
return ( m_pData->pActUndoArray != m_pData->pUndoArray );
}
@@ -705,6 +924,7 @@ bool SfxUndoManager::IsInListAction() const
USHORT SfxUndoManager::GetListActionDepth() const
{
+ UndoManagerGuard aGuard( *m_pData );
USHORT nDepth(0);
SfxUndoArray* pLookup( m_pData->pActUndoArray );
@@ -735,13 +955,15 @@ USHORT SfxUndoManager::LeaveAndMergeListAction()
USHORT SfxUndoManager::ImplLeaveListAction( const bool i_merge )
{
- if ( !IsUndoEnabled() )
+ UndoManagerGuard aGuard( *m_pData );
+
+ if ( !ImplIsUndoEnabled_Lock() )
return 0;
if ( !m_pData->pUndoArray->nMaxUndoActions )
return 0;
- if( !IsInListAction() )
+ if( !ImplIsInListAction_Lock() )
{
DBG_ERROR( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
return 0;
@@ -760,10 +982,9 @@ USHORT SfxUndoManager::ImplLeaveListAction( const bool i_merge )
{
SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ];
m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
- delete pCurrentAction;
+ aGuard.markForDeletion( pCurrentAction );
- ::std::for_each( m_pData->aListeners.begin(), m_pData->aListeners.end(),
- NotifyUndoListener( &SfxUndoListener::listActionCancelled ) );
+ aGuard.scheduleNotification( &SfxUndoListener::listActionCancelled );
return 0;
}
@@ -803,9 +1024,9 @@ USHORT SfxUndoManager::ImplLeaveListAction( const bool i_merge )
}
// notify listeners
- ::std::for_each( m_pData->aListeners.begin(), m_pData->aListeners.end(),
- NotifyUndoListener( &SfxUndoListener::listActionLeft ) );
+ aGuard.scheduleNotification( &SfxUndoListener::listActionLeft );
+ // outta here
return nListActionElements;
}