summaryrefslogtreecommitdiff
path: root/svl/source/undo
diff options
context:
space:
mode:
authorCarsten Driesner <cd@openoffice.org>2011-02-07 13:06:08 +0100
committerCarsten Driesner <cd@openoffice.org>2011-02-07 13:06:08 +0100
commitc39a5bda38b3af9ac6d964e6dff598e93121798a (patch)
tree9d681cb52e370b67568ec49a3175b0f6a1a3b509 /svl/source/undo
parentdc67e8e277d6ca8c3422cba0672157e461f9cc18 (diff)
parent99ff7a9fb521895d6ba18ca4ca92d7eb0c3524fd (diff)
removetooltypes01: Rebase to DEV300m99
Diffstat (limited to 'svl/source/undo')
-rw-r--r--svl/source/undo/undo.cxx1203
1 files changed, 943 insertions, 260 deletions
diff --git a/svl/source/undo/undo.cxx b/svl/source/undo/undo.cxx
index 5761e62c62cd..d277123637d7 100644
--- a/svl/source/undo/undo.cxx
+++ b/svl/source/undo/undo.cxx
@@ -30,10 +30,16 @@
#include <com/sun/star/uno/Exception.hpp>
+#include <comphelper/flagguard.hxx>
#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
#include <svl/undo.hxx>
+#include <vector>
+#include <list>
+#include <limits>
+
using ::com::sun::star::uno::Exception;
// STATIC DATA -----------------------------------------------------------
@@ -55,6 +61,12 @@ SfxRepeatTarget::~SfxRepeatTarget()
//------------------------------------------------------------------------
+SfxUndoContext::~SfxUndoContext()
+{
+}
+
+//------------------------------------------------------------------------
+
sal_Bool SfxUndoAction::IsLinked()
{
return bLinked;
@@ -117,7 +129,6 @@ XubString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
//------------------------------------------------------------------------
-
void SfxUndoAction::Undo()
{
// die sind nur konzeptuell pure virtual
@@ -126,6 +137,14 @@ void SfxUndoAction::Undo()
//------------------------------------------------------------------------
+void SfxUndoAction::UndoWithContext( SfxUndoContext& i_context )
+{
+ (void)i_context;
+ Undo();
+}
+
+//------------------------------------------------------------------------
+
void SfxUndoAction::Redo()
{
// die sind nur konzeptuell pure virtual
@@ -134,6 +153,14 @@ void SfxUndoAction::Redo()
//------------------------------------------------------------------------
+void SfxUndoAction::RedoWithContext( SfxUndoContext& i_context )
+{
+ (void)i_context;
+ Redo();
+}
+
+//------------------------------------------------------------------------
+
void SfxUndoAction::Repeat(SfxRepeatTarget&)
{
// die sind nur konzeptuell pure virtual
@@ -150,206 +177,554 @@ sal_Bool SfxUndoAction::CanRepeat(SfxRepeatTarget&) const
//========================================================================
+typedef ::std::vector< SfxUndoListener* > UndoListeners;
+
+struct SVL_DLLPRIVATE SfxUndoManager_Data
+{
+ ::osl::Mutex aMutex;
+ SfxUndoArray* pUndoArray;
+ SfxUndoArray* pActUndoArray;
+ SfxUndoArray* pFatherUndoArray;
+
+ sal_Int32 mnLockCount;
+ sal_Int32 mnMarks;
+ sal_Int32 mnEmptyMark;
+ bool mbDoing;
+ bool mbClearUntilTopLevel;
+
+ UndoListeners aListeners;
+
+ SfxUndoManager_Data( size_t i_nMaxUndoActionCount )
+ :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount ) )
+ ,pActUndoArray( NULL )
+ ,pFatherUndoArray( NULL )
+ ,mnLockCount( 0 )
+ ,mnMarks( 0 )
+ ,mnEmptyMark(MARK_INVALID)
+ ,mbDoing( false )
+ ,mbClearUntilTopLevel( false )
+ {
+ pActUndoArray = pUndoArray;
+ }
+
+ ~SfxUndoManager_Data()
+ {
+ delete pUndoArray;
+ }
+};
+
+//========================================================================
+
+namespace svl { namespace undo { namespace impl
+{
+ //--------------------------------------------------------------------
+ class SVL_DLLPRIVATE LockGuard
+ {
+ public:
+ LockGuard( SfxUndoManager& i_manager )
+ :m_manager( i_manager )
+ {
+ m_manager.ImplEnableUndo_Lock( false );
+ }
+
+ ~LockGuard()
+ {
+ m_manager.ImplEnableUndo_Lock( true );
+ }
+
+ private:
+ SfxUndoManager& m_manager;
+ };
+
+ //--------------------------------------------------------------------
+ typedef void ( SfxUndoListener::*UndoListenerVoidMethod )();
+ typedef void ( SfxUndoListener::*UndoListenerStringMethod )( const String& );
+
+ //--------------------------------------------------------------------
+ struct SVL_DLLPRIVATE NotifyUndoListener : public ::std::unary_function< SfxUndoListener*, void >
+ {
+ 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
+ {
+ OSL_PRECOND( is(), "NotifyUndoListener: this will crash!" );
+ if ( m_altNotificationMethod != NULL )
+ {
+ ( i_listener->*m_altNotificationMethod )( m_sActionComment );
+ }
+ else
+ {
+ ( i_listener->*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();
+ }
+
+ void cancelNotifications()
+ {
+ m_notifiers.clear();
+ }
+
+ /** 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;
+
+//========================================================================
+
+SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
+ :m_pData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
+{
+}
+
+//------------------------------------------------------------------------
-SfxUndoManager::SfxUndoManager( sal_uInt16 nMaxUndoActionCount )
- : pFatherUndoArray(0)
- , mbUndoEnabled( true )
+SfxUndoManager::~SfxUndoManager()
{
- pUndoArray=new SfxUndoArray(nMaxUndoActionCount);
- pActUndoArray=pUndoArray;
+ UndoListeners aListenersCopy;
+ {
+ UndoManagerGuard aGuard( *m_pData );
+ aListenersCopy = m_pData->aListeners;
+ }
+ ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(),
+ NotifyUndoListener( &SfxUndoListener::undoManagerDying ) );
}
//------------------------------------------------------------------------
+void SfxUndoManager::EnableUndo( bool i_enable )
+{
+ UndoManagerGuard aGuard( *m_pData );
+ ImplEnableUndo_Lock( i_enable );
-SfxUndoManager::~SfxUndoManager()
+}
+
+//------------------------------------------------------------------------
+
+void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
{
- delete pUndoArray;
+ if ( !i_enable )
+ ++m_pData->mnLockCount;
+ else
+ {
+ OSL_PRECOND( m_pData->mnLockCount > 0, "SfxUndoManager::ImplEnableUndo_NoNotify: not disabled, so why enabling?" );
+ if ( m_pData->mnLockCount > 0 )
+ --m_pData->mnLockCount;
+ }
}
//------------------------------------------------------------------------
-void SfxUndoManager::EnableUndo( bool bEnable )
+bool SfxUndoManager::IsUndoEnabled() const
{
- mbUndoEnabled = bEnable;
+ UndoManagerGuard aGuard( *m_pData );
+ return ImplIsUndoEnabled_Lock();
}
//------------------------------------------------------------------------
+bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
+{
+ return m_pData->mnLockCount == 0;
+}
+
+//------------------------------------------------------------------------
-void SfxUndoManager::SetMaxUndoActionCount( sal_uInt16 nMaxUndoActionCount )
+void SfxUndoManager::SetMaxUndoActionCount( size_t 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
// new nMaxUndoActionCount.
- long nNumToDelete = pActUndoArray->aUndoActions.Count() - nMaxUndoActionCount;
- if ( nNumToDelete > 0 )
+ long nNumToDelete = m_pData->pActUndoArray->aUndoActions.size() - nMaxUndoActionCount;
+ while ( nNumToDelete > 0 )
{
- while ( nNumToDelete > 0 )
+ size_t nPos = m_pData->pActUndoArray->aUndoActions.size();
+ if ( nPos > m_pData->pActUndoArray->nCurUndoAction )
{
- sal_uInt16 nPos = pActUndoArray->aUndoActions.Count();
- if ( nPos > pActUndoArray->nCurUndoAction )
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[nPos-1].pAction;
+ if ( !pAction->IsLinked() )
{
- if ( !pActUndoArray->aUndoActions[nPos-1]->IsLinked() )
- {
- delete pActUndoArray->aUndoActions[nPos-1];
- pActUndoArray->aUndoActions.Remove( nPos-1 );
- --nNumToDelete;
- }
+ aGuard.markForDeletion( pAction );
+ m_pData->pActUndoArray->aUndoActions.Remove( nPos-1 );
+ --nNumToDelete;
}
+ }
- if ( nNumToDelete > 0 && pActUndoArray->nCurUndoAction > 0 )
+ if ( nNumToDelete > 0 && m_pData->pActUndoArray->nCurUndoAction > 0 )
+ {
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
+ if ( !pAction->IsLinked() )
{
- if ( !pActUndoArray->aUndoActions[0]->IsLinked() )
- {
- delete pActUndoArray->aUndoActions[0];
- pActUndoArray->aUndoActions.Remove(0);
- --pActUndoArray->nCurUndoAction;
- --nNumToDelete;
- }
+ aGuard.markForDeletion( pAction );
+ m_pData->pActUndoArray->aUndoActions.Remove(0);
+ --m_pData->pActUndoArray->nCurUndoAction;
+ --nNumToDelete;
}
-
- if ( nPos == pActUndoArray->aUndoActions.Count() )
- break; // Cannot delete more entries
}
+
+ if ( nPos == m_pData->pActUndoArray->aUndoActions.size() )
+ break; // Cannot delete more entries
}
- pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
+ m_pData->pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
}
//------------------------------------------------------------------------
-sal_uInt16 SfxUndoManager::GetMaxUndoActionCount() const
+size_t SfxUndoManager::GetMaxUndoActionCount() const
{
- return pActUndoArray->nMaxUndoActions;
+ UndoManagerGuard aGuard( *m_pData );
+ return m_pData->pActUndoArray->nMaxUndoActions;
}
//------------------------------------------------------------------------
-void SfxUndoManager::Clear()
+void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard& i_guard )
{
- while ( pActUndoArray->aUndoActions.Count() )
+ // clear array
+ while ( !m_pData->pActUndoArray->aUndoActions.empty() )
{
- SfxUndoAction *pAction=
- pActUndoArray->aUndoActions[pActUndoArray->aUndoActions.Count() - 1];
- pActUndoArray->aUndoActions.Remove( pActUndoArray->aUndoActions.Count() - 1 );
- delete pAction;
+ size_t deletePos = m_pData->pActUndoArray->aUndoActions.size() - 1;
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ deletePos ].pAction;
+ i_guard.markForDeletion( pAction );
+ m_pData->pActUndoArray->aUndoActions.Remove( deletePos );
}
- pActUndoArray->nCurUndoAction = 0;
+ m_pData->pActUndoArray->nCurUndoAction = 0;
+
+ m_pData->mnMarks = 0;
+ m_pData->mnEmptyMark = MARK_INVALID;
}
//------------------------------------------------------------------------
-void SfxUndoManager::ClearRedo()
+void SfxUndoManager::Clear()
{
- while ( pActUndoArray->aUndoActions.Count() > pActUndoArray->nCurUndoAction )
- {
- SfxUndoAction *pAction=
- pActUndoArray->aUndoActions[pActUndoArray->aUndoActions.Count() - 1];
- pActUndoArray->aUndoActions.Remove( pActUndoArray->aUndoActions.Count() - 1 );
- delete pAction;
- }
+ UndoManagerGuard aGuard( *m_pData );
+
+ OSL_ENSURE( !ImplIsInListAction_Lock(), "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
+ ImplClearCurrentLevel_NoNotify( aGuard );
+
+ // notify listeners
+ aGuard.scheduleNotification( &SfxUndoListener::cleared );
}
//------------------------------------------------------------------------
-void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerge )
+void SfxUndoManager::ClearAllLevels()
{
- if( mbUndoEnabled )
+ UndoManagerGuard aGuard( *m_pData );
+ ImplClearCurrentLevel_NoNotify( aGuard );
+
+ if ( ImplIsInListAction_Lock() )
+ {
+ m_pData->mbClearUntilTopLevel = true;
+ }
+ else
{
- // Redo-Actions loeschen
- for ( sal_uInt16 nPos = pActUndoArray->aUndoActions.Count();
- nPos > pActUndoArray->nCurUndoAction; --nPos )
- delete pActUndoArray->aUndoActions[nPos-1];
+ aGuard.scheduleNotification( &SfxUndoListener::cleared );
+ }
+}
- pActUndoArray->aUndoActions.Remove(
- pActUndoArray->nCurUndoAction,
- pActUndoArray->aUndoActions.Count() - pActUndoArray->nCurUndoAction );
+//------------------------------------------------------------------------
- if ( pActUndoArray->nMaxUndoActions )
- {
- SfxUndoAction *pTmpAction = pActUndoArray->nCurUndoAction ?
- pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction-1] : 0;
+void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
+{
+ UndoManagerGuard aGuard( *m_pData );
+ ImplClearRedo( aGuard, i_currentLevel );
+}
- if ( !bTryMerge || !(pTmpAction && pTmpAction->Merge(pAction)) )
- {
- // auf Max-Anzahl anpassen
- if( pActUndoArray == pUndoArray )
- while( pActUndoArray->aUndoActions.Count() >=
- pActUndoArray->nMaxUndoActions &&
- !pActUndoArray->aUndoActions[0]->IsLinked() )
- {
- delete pActUndoArray->aUndoActions[0];
- pActUndoArray->aUndoActions.Remove(0);
- --pActUndoArray->nCurUndoAction;
- }
-
- // neue Action anh"angen
- const SfxUndoAction* pTemp = pAction;
- pActUndoArray->aUndoActions.Insert(
- pTemp, pActUndoArray->nCurUndoAction++ );
- return;
- }
- }
- }
- delete pAction;
+//------------------------------------------------------------------------
+
+void SfxUndoManager::ClearRedo()
+{
+ OSL_ENSURE( !IsInListAction(), "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
+ ImplClearRedo_NoLock( CurrentLevel );
}
//------------------------------------------------------------------------
-sal_uInt16 SfxUndoManager::GetUndoActionCount() const
+void SfxUndoManager::Reset()
{
- return pActUndoArray->nCurUndoAction;
+ UndoManagerGuard aGuard( *m_pData );
+
+ // clear all locks
+ while ( !ImplIsUndoEnabled_Lock() )
+ ImplEnableUndo_Lock( true );
+
+ // cancel all list actions
+ while ( IsInListAction() )
+ ImplLeaveListAction( false, aGuard );
+
+ // clear both stacks
+ ImplClearCurrentLevel_NoNotify( aGuard );
+
+ // cancel the notifications scheduled by ImplLeaveListAction,
+ // as we want to do an own, dedicated notification
+ aGuard.cancelNotifications();
+
+ // schedule notification
+ aGuard.scheduleNotification( &SfxUndoListener::resetAll );
}
//------------------------------------------------------------------------
-XubString SfxUndoManager::GetUndoActionComment( sal_uInt16 nNo ) const
+void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
{
- DBG_ASSERT( nNo < pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment(), illegal id!" );
- if( nNo < pActUndoArray->nCurUndoAction )
+ while ( m_pData->pActUndoArray->nCurUndoAction > 0 )
{
- return pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction-1-nNo]->GetComment(); //!
+ SfxUndoAction* pUndoAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
+ m_pData->pActUndoArray->aUndoActions.Remove( 0 );
+ i_guard.markForDeletion( pUndoAction );
+ --m_pData->pActUndoArray->nCurUndoAction;
}
- else
+ // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
+}
+
+//------------------------------------------------------------------------
+
+void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard, bool const i_currentLevel )
+{
+ SfxUndoArray* pUndoArray = ( i_currentLevel == IUndoManager::CurrentLevel ) ? m_pData->pActUndoArray : m_pData->pUndoArray;
+
+ // clearance
+ while ( pUndoArray->aUndoActions.size() > pUndoArray->nCurUndoAction )
{
- XubString aEmpty;
- return aEmpty;
+ size_t deletePos = pUndoArray->aUndoActions.size() - 1;
+ SfxUndoAction* pAction = pUndoArray->aUndoActions[ deletePos ].pAction;
+ pUndoArray->aUndoActions.Remove( deletePos );
+ i_guard.markForDeletion( pAction );
}
+
+ // notification - only if the top level's stack was cleared
+ if ( i_currentLevel == IUndoManager::TopLevel )
+ i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
}
//------------------------------------------------------------------------
-sal_uInt16 SfxUndoManager::GetUndoActionId( sal_uInt16 nNo ) const
+bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction *pAction, bool bTryMerge, bool bClearRedo, UndoManagerGuard& i_guard )
{
- DBG_ASSERT( nNo < pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionId(), illegal id!" );
- if( nNo < pActUndoArray->nCurUndoAction )
+ if ( !ImplIsUndoEnabled_Lock() || ( m_pData->pActUndoArray->nMaxUndoActions == 0 ) )
{
- return pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction-1-nNo]->GetId(); //!
+ i_guard.markForDeletion( pAction );
+ return false;
}
- else
+
+ // merge, if required
+ SfxUndoAction* pMergeWithAction = m_pData->pActUndoArray->nCurUndoAction ?
+ m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction : NULL;
+ if ( bTryMerge && ( !pMergeWithAction || !pMergeWithAction->Merge( pAction ) ) )
{
- return 0;
+ i_guard.markForDeletion( pAction );
+ return false;
}
+
+ // clear redo stack, if requested
+ if ( bClearRedo && ( ImplGetRedoActionCount_Lock( CurrentLevel ) > 0 ) )
+ ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
+
+ // respect max number
+ if( m_pData->pActUndoArray == m_pData->pUndoArray )
+ {
+ while( m_pData->pActUndoArray->aUndoActions.size() >=
+ m_pData->pActUndoArray->nMaxUndoActions &&
+ !m_pData->pActUndoArray->aUndoActions[0].pAction->IsLinked() )
+ {
+ i_guard.markForDeletion( m_pData->pActUndoArray->aUndoActions[0].pAction );
+ m_pData->pActUndoArray->aUndoActions.Remove(0);
+ --m_pData->pActUndoArray->nCurUndoAction;
+ }
+ }
+
+ // append new action
+ m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ );
+ return true;
}
//------------------------------------------------------------------------
-SfxUndoAction* SfxUndoManager::GetUndoAction( sal_uInt16 nNo ) const
+void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerge )
{
- DBG_ASSERT( nNo < pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
- if( nNo < pActUndoArray->nCurUndoAction )
+ UndoManagerGuard aGuard( *m_pData );
+
+ // add
+ if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
{
- return pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction-1-nNo]; //!
+ // notify listeners
+ aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
}
- else
+}
+
+//------------------------------------------------------------------------
+
+size_t 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;
+}
+
+//------------------------------------------------------------------------
+
+XubString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
+{
+ 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 )
{
- return 0;
+ sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
}
+ return sComment;
+}
+
+//------------------------------------------------------------------------
+
+sal_uInt16 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 NULL;
+ return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction->GetId();
+}
+
+//------------------------------------------------------------------------
+
+SfxUndoAction* SfxUndoManager::GetUndoAction( size_t 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 NULL;
+ return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1-nNo].pAction;
}
//------------------------------------------------------------------------
@@ -357,148 +732,221 @@ SfxUndoAction* SfxUndoManager::GetUndoAction( sal_uInt16 nNo ) const
/** clears the redo stack and removes the top undo action */
void SfxUndoManager::RemoveLastUndoAction()
{
- DBG_ASSERT( pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
- if( pActUndoArray->nCurUndoAction )
- {
- 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
- sal_uInt16 nPos;
- for ( nPos = pActUndoArray->aUndoActions.Count(); nPos > pActUndoArray->nCurUndoAction; --nPos )
- delete pActUndoArray->aUndoActions[nPos-1];
+ m_pData->pActUndoArray->nCurUndoAction--;
- pActUndoArray->aUndoActions.Remove(
- pActUndoArray->nCurUndoAction,
- pActUndoArray->aUndoActions.Count() - pActUndoArray->nCurUndoAction );
+ // delete redo-actions and top action
+ for ( size_t nPos = m_pData->pActUndoArray->aUndoActions.size(); nPos > m_pData->pActUndoArray->nCurUndoAction; --nPos )
+ {
+ aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[nPos-1].pAction );
}
+
+ m_pData->pActUndoArray->aUndoActions.Remove(
+ m_pData->pActUndoArray->nCurUndoAction,
+ m_pData->pActUndoArray->aUndoActions.size() - m_pData->pActUndoArray->nCurUndoAction );
}
//------------------------------------------------------------------------
-sal_Bool SfxUndoManager::Undo( sal_uInt16 )
+bool SfxUndoManager::IsDoing() const
{
- bool bUndoWasEnabled = mbUndoEnabled;
- mbUndoEnabled = false;
+ UndoManagerGuard aGuard( *m_pData );
+ return m_pData->mbDoing;
+}
- sal_Bool bRet = sal_False;
+//------------------------------------------------------------------------
- try
- {
- DBG_ASSERT( pActUndoArray == pUndoArray, "svl::SfxUndoManager::Undo(), LeaveListAction() not yet called!" );
- if ( pActUndoArray->nCurUndoAction )
- {
- Undo( *pActUndoArray->aUndoActions[ --pActUndoArray->nCurUndoAction ] );
- bRet = sal_True;
- }
- }
- catch( Exception& e )
- {
- mbUndoEnabled = bUndoWasEnabled;
- throw e;
- }
- mbUndoEnabled = bUndoWasEnabled;
- return bRet;
+BOOL SfxUndoManager::Undo()
+{
+ return ImplUndo( NULL );
}
//------------------------------------------------------------------------
-void SfxUndoManager::Undo( SfxUndoAction &rAction )
+sal_Bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
{
- bool bUndoWasEnabled = mbUndoEnabled;
- mbUndoEnabled = false;
+ return ImplUndo( &i_context );
+}
+
+//------------------------------------------------------------------------
+
+sal_Bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
+{
+ UndoManagerGuard aGuard( *m_pData );
+ OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
+
+ ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
+ LockGuard aLockGuard( *this );
+
+ if ( ImplIsInListAction_Lock() )
+ {
+ OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" );
+ return sal_False;
+ }
+
+ if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
+ {
+ OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" );
+ return sal_False;
+ }
+
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ].pAction;
+ const String sActionComment = pAction->GetComment();
try
{
- rAction.Undo();
+ // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
+ // nowadays ...
+ aGuard.clear();
+ if ( i_contextOrNull != NULL )
+ pAction->UndoWithContext( *i_contextOrNull );
+ else
+ pAction->Undo();
+ aGuard.reset();
}
- catch( Exception& e )
+ catch( ... )
{
- mbUndoEnabled = bUndoWasEnabled;
- throw e;
+ 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
+ size_t nCurAction = 0;
+ while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
+ {
+ if ( m_pData->pActUndoArray->aUndoActions[ nCurAction++ ].pAction == pAction )
+ {
+ // the Undo action is still there ...
+ // assume the error is a permanent failure, and clear the Undo stack
+ ImplClearUndo( aGuard );
+ throw;
+ }
+ }
+ OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
+ throw;
}
- mbUndoEnabled = bUndoWasEnabled;
+ aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
+
+ return TRUE;
}
//------------------------------------------------------------------------
-sal_uInt16 SfxUndoManager::GetRedoActionCount() const
+size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
{
- return pActUndoArray->aUndoActions.Count() - pActUndoArray->nCurUndoAction; //!
+ UndoManagerGuard aGuard( *m_pData );
+ return ImplGetRedoActionCount_Lock( i_currentLevel );
}
//------------------------------------------------------------------------
-XubString SfxUndoManager::GetRedoActionComment( sal_uInt16 nNo ) const
+size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
{
- return pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction+nNo]->GetComment(); //!
+ const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
+ return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction;
}
//------------------------------------------------------------------------
-sal_uInt16 SfxUndoManager::GetRedoActionId( sal_uInt16 nNo ) const
+XubString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
{
- return pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction+nNo]->GetId(); //!
+ UndoManagerGuard aGuard( *m_pData );
+ const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
+ return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
}
//------------------------------------------------------------------------
-sal_Bool SfxUndoManager::Redo( sal_uInt16 )
+sal_Bool SfxUndoManager::Redo()
{
- bool bUndoWasEnabled = mbUndoEnabled;
- mbUndoEnabled = false;
-
- sal_Bool bRet = sal_False;
+ return ImplRedo( NULL );
+}
- try
- {
- if ( pActUndoArray->aUndoActions.Count() > pActUndoArray->nCurUndoAction )
- {
- Redo( *pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction++] );
- bRet = sal_True;
- }
- }
- catch( Exception& e )
- {
- mbUndoEnabled = bUndoWasEnabled;
- throw e;
- }
+//------------------------------------------------------------------------
- mbUndoEnabled = bUndoWasEnabled;
- return bRet;
+sal_Bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
+{
+ return ImplRedo( &i_context );
}
//------------------------------------------------------------------------
-void SfxUndoManager::Redo( SfxUndoAction &rAction )
+BOOL SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
{
- bool bUndoWasEnabled = mbUndoEnabled;
- mbUndoEnabled = false;
+ UndoManagerGuard aGuard( *m_pData );
+ OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
+
+ ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
+ LockGuard aLockGuard( *this );
+ if ( ImplIsInListAction_Lock() )
+ {
+ OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
+ return FALSE;
+ }
+
+ if ( m_pData->pActUndoArray->nCurUndoAction >= m_pData->pActUndoArray->aUndoActions.size() )
+ {
+ OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" );
+ return FALSE;
+ }
+
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction;
+ const String sActionComment = pAction->GetComment();
try
{
- rAction.Redo();
+ // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
+ // nowadays ...
+ aGuard.clear();
+ if ( i_contextOrNull != NULL )
+ pAction->RedoWithContext( *i_contextOrNull );
+ else
+ pAction->Redo();
+ aGuard.reset();
}
- catch( Exception& e )
+ catch( ... )
{
- mbUndoEnabled = bUndoWasEnabled;
- throw e;
+ 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
+ size_t nCurAction = 0;
+ while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
+ {
+ if ( m_pData->pActUndoArray->aUndoActions[ nCurAction ].pAction == pAction )
+ {
+ // the Undo action is still there ...
+ // assume the error is a permanent failure, and clear the Undo stack
+ ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
+ throw;
+ }
+ ++nCurAction;
+ }
+ OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
+ throw;
}
- mbUndoEnabled = bUndoWasEnabled;
+ aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
+
+ return TRUE;
}
//------------------------------------------------------------------------
-sal_uInt16 SfxUndoManager::GetRepeatActionCount() const
+size_t SfxUndoManager::GetRepeatActionCount() const
{
- return pActUndoArray->aUndoActions.Count();
+ UndoManagerGuard aGuard( *m_pData );
+ return m_pData->pActUndoArray->aUndoActions.size();
}
//------------------------------------------------------------------------
-XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget, sal_uInt16 nNo ) const
+XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) const
{
- return pActUndoArray->aUndoActions[ pActUndoArray->aUndoActions.Count() - 1 - nNo ]
+ UndoManagerGuard aGuard( *m_pData );
+ return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction
->GetRepeatComment(rTarget);
}
@@ -506,10 +954,14 @@ XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget, sal_
sal_Bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget, sal_uInt16 /*nFrom*/, sal_uInt16 /*nCount*/ )
{
- if ( pActUndoArray->aUndoActions.Count() )
+ UndoManagerGuard aGuard( *m_pData );
+ if ( !m_pData->pActUndoArray->aUndoActions.empty() )
{
- Repeat( rTarget, *pActUndoArray->aUndoActions[ pActUndoArray->aUndoActions.Count() - 1 ] );
- return sal_True;
+ SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction;
+ aGuard.clear();
+ if ( pAction->CanRepeat( rTarget ) )
+ pAction->Repeat( rTarget );
+ return TRUE;
}
return sal_False;
@@ -517,30 +969,41 @@ sal_Bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget, sal_uInt16 /*nFrom*/,
//------------------------------------------------------------------------
-void SfxUndoManager::Repeat( SfxRepeatTarget &rTarget, SfxUndoAction &rAction )
+BOOL SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
{
- if ( rAction.CanRepeat(rTarget) )
- rAction.Repeat(rTarget);
+ UndoManagerGuard aGuard( *m_pData );
+ if ( !m_pData->pActUndoArray->aUndoActions.empty() )
+ {
+ size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1;
+ return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
+ }
+ return FALSE;
}
//------------------------------------------------------------------------
-sal_Bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget, SfxUndoAction &rAction ) const
+void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
{
- return rAction.CanRepeat(rTarget);
+ UndoManagerGuard aGuard( *m_pData );
+ m_pData->aListeners.push_back( &i_listener );
}
//------------------------------------------------------------------------
-sal_Bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget, sal_uInt16 nNo ) const
+void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
{
- if ( pActUndoArray->aUndoActions.Count() > nNo )
+ UndoManagerGuard aGuard( *m_pData );
+ for ( UndoListeners::iterator lookup = m_pData->aListeners.begin();
+ lookup != m_pData->aListeners.end();
+ ++lookup
+ )
{
- sal_uInt16 nActionNo = pActUndoArray->aUndoActions.Count() - 1 - nNo;
- return pActUndoArray->aUndoActions[nActionNo]->CanRepeat(rTarget);
+ if ( (*lookup) == &i_listener )
+ {
+ m_pData->aListeners.erase( lookup );
+ break;
+ }
}
-
- return sal_False;
}
//------------------------------------------------------------------------
@@ -554,69 +1017,270 @@ void SfxUndoManager::EnterListAction(
*/
{
- if( !mbUndoEnabled )
+ UndoManagerGuard aGuard( *m_pData );
+
+ if( !ImplIsUndoEnabled_Lock() )
return;
- if ( !pUndoArray->nMaxUndoActions )
+ if ( !m_pData->pUndoArray->nMaxUndoActions )
return;
- pFatherUndoArray=pActUndoArray;
- SfxListUndoAction *pAction=new SfxListUndoAction(
- rComment, rRepeatComment, nId, pActUndoArray);
- AddUndoAction( pAction );
- pActUndoArray=pAction;
+ m_pData->pFatherUndoArray = m_pData->pActUndoArray;
+ SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
+ OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) );
+ // expected to succeed: all conditions under which it could fail should have been checked already
+ m_pData->pActUndoArray = pAction;
+
+ // notification
+ aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
}
//------------------------------------------------------------------------
-void SfxUndoManager::LeaveListAction()
+bool SfxUndoManager::IsInListAction() const
+{
+ UndoManagerGuard aGuard( *m_pData );
+ return ImplIsInListAction_Lock();
+}
-/* [Beschreibung]
+//------------------------------------------------------------------------
- Verlaesst die aktuelle ListAction und geht eine Ebene nach oben.
-*/
+bool SfxUndoManager::ImplIsInListAction_Lock() const
{
- if ( !mbUndoEnabled )
- return;
+ return ( m_pData->pActUndoArray != m_pData->pUndoArray );
+}
- if ( !pUndoArray->nMaxUndoActions )
- return;
+//------------------------------------------------------------------------
+
+size_t SfxUndoManager::GetListActionDepth() const
+{
+ UndoManagerGuard aGuard( *m_pData );
+ size_t nDepth(0);
- if( pActUndoArray == pUndoArray )
+ SfxUndoArray* pLookup( m_pData->pActUndoArray );
+ while ( pLookup != m_pData->pUndoArray )
{
- DBG_ERROR( "svl::SfxUndoManager::LeaveListAction(), called without calling EnterListAction()!" );
- return;
+ pLookup = pLookup->pFatherUndoArray;
+ ++nDepth;
}
- DBG_ASSERT(pActUndoArray->pFatherUndoArray,"svl::SfxUndoManager::LeaveListAction(), no father undo array!?");
+ return nDepth;
+}
+
+//------------------------------------------------------------------------
- SfxUndoArray* pTmp=pActUndoArray;
- pActUndoArray=pActUndoArray->pFatherUndoArray;
+size_t SfxUndoManager::LeaveListAction()
+{
+ UndoManagerGuard aGuard( *m_pData );
+ size_t nCount = ImplLeaveListAction( false, aGuard );
- // If no undo action where added, delete the undo list action
- SfxUndoAction *pTmpAction= pActUndoArray->aUndoActions[pActUndoArray->nCurUndoAction-1];
- if(!pTmp->nCurUndoAction)
+ if ( m_pData->mbClearUntilTopLevel )
{
- pActUndoArray->aUndoActions.Remove( --pActUndoArray->nCurUndoAction);
- delete pTmpAction;
+ ImplClearCurrentLevel_NoNotify( aGuard );
+ if ( !ImplIsInListAction_Lock() )
+ {
+ m_pData->mbClearUntilTopLevel = false;
+ aGuard.scheduleNotification( &SfxUndoListener::cleared );
+ }
+ nCount = 0;
}
- else
+
+ return nCount;
+}
+
+//------------------------------------------------------------------------
+
+size_t SfxUndoManager::LeaveAndMergeListAction()
+{
+ UndoManagerGuard aGuard( *m_pData );
+ return ImplLeaveListAction( true, aGuard );
+}
+
+//------------------------------------------------------------------------
+
+size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
+{
+ if ( !ImplIsUndoEnabled_Lock() )
+ return 0;
+
+ if ( !m_pData->pUndoArray->nMaxUndoActions )
+ return 0;
+
+ if( !ImplIsInListAction_Lock() )
{
- // if the undo array has no comment, try to get it from its children
- SfxListUndoAction* pList = dynamic_cast< SfxListUndoAction * >( pTmpAction );
- if( pList && pList->GetComment().Len() == 0 )
+ DBG_ERROR( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
+ return 0;
+ }
+
+ DBG_ASSERT( m_pData->pActUndoArray->pFatherUndoArray, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" );
+
+ // the array/level which we're about to leave
+ SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray;
+ // one step up
+ m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray;
+
+ // If no undo actions were added to the list, delete the list action
+ const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
+ if ( nListActionElements == 0 )
+ {
+ SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
+ m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
+ i_guard.markForDeletion( pCurrentAction );
+
+ i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
+ return 0;
+ }
+
+ // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
+ // the redo stack
+ ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
+
+ SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
+ SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
+ ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
+
+ if ( i_merge )
+ {
+ // merge the list action with its predecessor on the same level
+ OSL_ENSURE( m_pData->pActUndoArray->nCurUndoAction > 1,
+ "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
+ if ( m_pData->pActUndoArray->nCurUndoAction > 1 )
{
- sal_uInt16 n;
- for( n = 0; n < pList->aUndoActions.Count(); n++ )
+ SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction;
+ m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 );
+ --m_pData->pActUndoArray->nCurUndoAction;
+ pListAction->aUndoActions.Insert( pPreviousAction, 0 );
+ ++pListAction->nCurUndoAction;
+
+ pListAction->SetComment( pPreviousAction->GetComment() );
+ }
+ }
+
+ // if the undo array has no comment, try to get it from its children
+ if ( pListAction->GetComment().Len() == 0 )
+ {
+ for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
+ {
+ if( pListAction->aUndoActions[n].pAction->GetComment().Len() )
{
- if( pList->aUndoActions[n]->GetComment().Len() )
- {
- pList->SetComment( pList->aUndoActions[n]->GetComment() );
- break;
- }
+ pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
+ break;
}
}
}
+
+ // notify listeners
+ i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
+
+ // outta here
+ return nListActionElements;
+}
+
+//------------------------------------------------------------------------
+UndoStackMark SfxUndoManager::MarkTopUndoAction()
+{
+ UndoManagerGuard aGuard( *m_pData );
+
+ OSL_ENSURE( !IsInListAction(),
+ "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
+ OSL_ENSURE((m_pData->mnMarks + 1) < (m_pData->mnEmptyMark - 1),
+ "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
+
+ size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction;
+ if (0 == nActionPos)
+ {
+ --m_pData->mnEmptyMark;
+ return m_pData->mnEmptyMark;
+ }
+
+ m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
+ ++m_pData->mnMarks );
+ return m_pData->mnMarks;
+}
+
+//------------------------------------------------------------------------
+void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
+{
+ UndoManagerGuard aGuard( *m_pData );
+
+ if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
+ {
+ return; // nothing to remove
+ }
+ else if (i_mark == m_pData->mnEmptyMark)
+ {
+ --m_pData->mnEmptyMark; // never returned from MarkTop => invalid
+ return;
+ }
+
+ for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i )
+ {
+ MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i];
+ for ( ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
+ markPos != rAction.aMarks.end();
+ ++markPos
+ )
+ {
+ if ( *markPos == i_mark )
+ {
+ rAction.aMarks.erase( markPos );
+ return;
+ }
+ }
+ }
+ OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" );
+ // TODO: this might be too offensive. There are situations where we implicitly remove marks
+ // without our clients, in particular the client which created the mark, having a chance to know
+ // about this.
+}
+
+//------------------------------------------------------------------------
+bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
+{
+ UndoManagerGuard aGuard( *m_pData );
+
+ size_t nActionPos = m_pData->pUndoArray->nCurUndoAction;
+ if ( nActionPos == 0 )
+ {
+ return (i_mark == m_pData->mnEmptyMark);
+ }
+
+ const MarkedUndoAction& rAction =
+ m_pData->pUndoArray->aUndoActions[ nActionPos-1 ];
+ for ( ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
+ markPos != rAction.aMarks.end();
+ ++markPos
+ )
+ {
+ if ( *markPos == i_mark )
+ return true;
+ }
+
+ return false;
+}
+
+//------------------------------------------------------------------------
+
+void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count )
+{
+ UndoManagerGuard aGuard( *m_pData );
+
+ size_t nActionsToRemove = i_count;
+ while ( nActionsToRemove )
+ {
+ SfxUndoAction* pActionToRemove = m_pData->pUndoArray->aUndoActions[0].pAction;
+
+ if ( IsInListAction() && ( m_pData->pUndoArray->nCurUndoAction == 1 ) )
+ {
+ OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" );
+ return;
+ }
+
+ aGuard.markForDeletion( pActionToRemove );
+ m_pData->pUndoArray->aUndoActions.Remove( 0 );
+ --m_pData->pUndoArray->nCurUndoAction;
+ --nActionsToRemove;
+ }
}
//------------------------------------------------------------------------
@@ -667,8 +1331,17 @@ SfxListUndoAction::SfxListUndoAction
void SfxListUndoAction::Undo()
{
- for(sal_Int16 i=nCurUndoAction-1;i>=0;i--)
- aUndoActions[i]->Undo();
+ for(size_t i=nCurUndoAction;i>0;)
+ aUndoActions[--i].pAction->Undo();
+ nCurUndoAction=0;
+}
+
+//------------------------------------------------------------------------
+
+void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
+{
+ for(size_t i=nCurUndoAction;i>0;)
+ aUndoActions[--i].pAction->UndoWithContext( i_context );
nCurUndoAction=0;
}
@@ -676,25 +1349,34 @@ void SfxListUndoAction::Undo()
void SfxListUndoAction::Redo()
{
- for(sal_uInt16 i=nCurUndoAction;i<aUndoActions.Count();i++)
- aUndoActions[i]->Redo();
- nCurUndoAction = aUndoActions.Count();
+ for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
+ aUndoActions[i].pAction->Redo();
+ nCurUndoAction = aUndoActions.size();
+}
+
+//------------------------------------------------------------------------
+
+void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
+{
+ for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
+ aUndoActions[i].pAction->RedoWithContext( i_context );
+ nCurUndoAction = aUndoActions.size();
}
//------------------------------------------------------------------------
void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
{
- for(sal_uInt16 i=0;i<nCurUndoAction;i++)
- aUndoActions[i]->Repeat(rTarget);
+ for(size_t i=0;i<nCurUndoAction;i++)
+ aUndoActions[i].pAction->Repeat(rTarget);
}
//------------------------------------------------------------------------
sal_Bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r) const
{
- for(sal_uInt16 i=0;i<nCurUndoAction;i++)
- if(!aUndoActions[i]->CanRepeat(r))
+ for(size_t i=0;i<nCurUndoAction;i++)
+ if(!aUndoActions[i].pAction->CanRepeat(r))
return sal_False;
return sal_True;
}
@@ -703,12 +1385,12 @@ sal_Bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r) const
sal_Bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
{
- return aUndoActions.Count() && aUndoActions[aUndoActions.Count()-1]->Merge( pNextAction );
+ return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction );
}
//------------------------------------------------------------------------
-SfxLinkUndoAction::SfxLinkUndoAction(SfxUndoManager *pManager)
+SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager)
/* [Beschreibung]
Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt.
@@ -718,10 +1400,15 @@ SfxLinkUndoAction::SfxLinkUndoAction(SfxUndoManager *pManager)
{
pUndoManager = pManager;
+ SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager );
+ ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" );
+ // yes, this cast is dirty. But reaching into the the SfxUndoManager's implementation,
+ // directly accessing its internal stack, and tampering with an action on that stack
+ // is dirty, too.
if ( pManager->GetMaxUndoActionCount() )
{
- sal_uInt16 nPos = pManager->GetUndoActionCount()-1;
- pAction = pManager->pActUndoArray->aUndoActions[nPos];
+ size_t nPos = pManager->GetUndoActionCount()-1;
+ pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction;
pAction->SetLinked();
}
else
@@ -733,7 +1420,7 @@ SfxLinkUndoAction::SfxLinkUndoAction(SfxUndoManager *pManager)
void SfxLinkUndoAction::Undo()
{
if ( pAction )
- pUndoManager->Undo(1);
+ pUndoManager->Undo();
}
//------------------------------------------------------------------------
@@ -741,7 +1428,7 @@ void SfxLinkUndoAction::Undo()
void SfxLinkUndoAction::Redo()
{
if ( pAction )
- pUndoManager->Redo(1);
+ pUndoManager->Redo();
}
//------------------------------------------------------------------------
@@ -749,7 +1436,7 @@ void SfxLinkUndoAction::Redo()
sal_Bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
{
- return pAction && pUndoManager->CanRepeat(r,*pAction);
+ return pAction && pAction->CanRepeat(r);
}
@@ -758,8 +1445,8 @@ sal_Bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r)
{
- if ( pAction )
- pUndoManager->Repeat(r,*pAction);
+ if ( pAction && pAction->CanRepeat( r ) )
+ pAction->Repeat( r );
}
@@ -797,11 +1484,10 @@ SfxLinkUndoAction::~SfxLinkUndoAction()
SfxUndoArray::~SfxUndoArray()
{
- while ( aUndoActions.Count() )
+ while ( !aUndoActions.empty() )
{
- SfxUndoAction *pAction =
- aUndoActions[ aUndoActions.Count() - 1 ];
- aUndoActions.Remove( aUndoActions.Count() - 1 );
+ SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
+ aUndoActions.Remove( aUndoActions.size() - 1 );
delete pAction;
}
}
@@ -811,6 +1497,3 @@ sal_uInt16 SfxLinkUndoAction::GetId() const
{
return pAction ? pAction->GetId() : 0;
}
-
-
-