1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <com/sun/star/uno/Exception.hpp>
23 #include <comphelper/flagguard.hxx>
24 #include <tools/debug.hxx>
25 #include <tools/diagnose_ex.h>
27 #include <svl/undo.hxx>
33 using ::com::sun::star::uno::Exception
;
35 // STATIC DATA -----------------------------------------------------------
37 DBG_NAME(SfxUndoAction
)
39 //========================================================================
41 TYPEINIT0(SfxUndoAction
);
42 TYPEINIT0(SfxListUndoAction
);
43 TYPEINIT0(SfxLinkUndoAction
);
44 TYPEINIT0(SfxRepeatTarget
);
46 //------------------------------------------------------------------------
48 SfxRepeatTarget::~SfxRepeatTarget()
52 //------------------------------------------------------------------------
54 SfxUndoContext::~SfxUndoContext()
58 //------------------------------------------------------------------------
60 sal_Bool
SfxUndoAction::IsLinked()
65 //------------------------------------------------------------------------
67 void SfxUndoAction::SetLinked( sal_Bool bIsLinked
)
72 //------------------------------------------------------------------------
74 SfxUndoAction::~SfxUndoAction()
76 DBG_DTOR(SfxUndoAction
, 0);
77 DBG_ASSERT( !IsLinked(), "Gelinkte Action geloescht" );
81 SfxUndoAction::SfxUndoAction()
83 DBG_CTOR(SfxUndoAction
, 0);
84 SetLinked( sal_False
);
87 //------------------------------------------------------------------------
89 sal_Bool
SfxUndoAction::Merge( SfxUndoAction
* )
91 DBG_CHKTHIS(SfxUndoAction
, 0);
95 //------------------------------------------------------------------------
97 OUString
SfxUndoAction::GetComment() const
99 DBG_CHKTHIS(SfxUndoAction
, 0);
103 //------------------------------------------------------------------------
106 sal_uInt16
SfxUndoAction::GetId() const
108 DBG_CHKTHIS(SfxUndoAction
, 0);
112 //------------------------------------------------------------------------
114 OUString
SfxUndoAction::GetRepeatComment(SfxRepeatTarget
&) const
116 DBG_CHKTHIS(SfxUndoAction
, 0);
120 //------------------------------------------------------------------------
122 void SfxUndoAction::Undo()
124 // die sind nur konzeptuell pure virtual
125 OSL_FAIL( "pure virtual function called: SfxUndoAction::Undo()" );
128 //------------------------------------------------------------------------
130 void SfxUndoAction::UndoWithContext( SfxUndoContext
& i_context
)
136 //------------------------------------------------------------------------
138 void SfxUndoAction::Redo()
140 // die sind nur konzeptuell pure virtual
141 OSL_FAIL( "pure virtual function called: SfxUndoAction::Redo()" );
144 //------------------------------------------------------------------------
146 void SfxUndoAction::RedoWithContext( SfxUndoContext
& i_context
)
152 //------------------------------------------------------------------------
154 void SfxUndoAction::Repeat(SfxRepeatTarget
&)
156 // die sind nur konzeptuell pure virtual
157 OSL_FAIL( "pure virtual function called: SfxUndoAction::Repeat()" );
160 //------------------------------------------------------------------------
163 sal_Bool
SfxUndoAction::CanRepeat(SfxRepeatTarget
&) const
168 //========================================================================
170 typedef ::std::vector
< SfxUndoListener
* > UndoListeners
;
172 struct SVL_DLLPRIVATE SfxUndoManager_Data
175 SfxUndoArray
* pUndoArray
;
176 SfxUndoArray
* pActUndoArray
;
177 SfxUndoArray
* pFatherUndoArray
;
180 sal_Int32 mnEmptyMark
;
183 bool mbClearUntilTopLevel
;
185 UndoListeners aListeners
;
187 SfxUndoManager_Data( size_t i_nMaxUndoActionCount
)
188 :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount
) )
189 ,pActUndoArray( NULL
)
190 ,pFatherUndoArray( NULL
)
192 ,mnEmptyMark(MARK_INVALID
)
193 ,mbUndoEnabled( true )
195 ,mbClearUntilTopLevel( false )
197 pActUndoArray
= pUndoArray
;
200 ~SfxUndoManager_Data()
206 //========================================================================
208 namespace svl
{ namespace undo
{ namespace impl
210 //--------------------------------------------------------------------
211 class SVL_DLLPRIVATE LockGuard
214 LockGuard( SfxUndoManager
& i_manager
)
215 :m_manager( i_manager
)
217 m_manager
.ImplEnableUndo_Lock( false );
222 m_manager
.ImplEnableUndo_Lock( true );
226 SfxUndoManager
& m_manager
;
229 //--------------------------------------------------------------------
230 typedef void ( SfxUndoListener::*UndoListenerVoidMethod
)();
231 typedef void ( SfxUndoListener::*UndoListenerStringMethod
)( const String
& );
233 //--------------------------------------------------------------------
234 struct SVL_DLLPRIVATE NotifyUndoListener
: public ::std::unary_function
< SfxUndoListener
*, void >
237 :m_notificationMethod( NULL
)
238 ,m_altNotificationMethod( NULL
)
243 NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod
)
244 :m_notificationMethod( i_notificationMethod
)
245 ,m_altNotificationMethod( NULL
)
250 NotifyUndoListener( UndoListenerStringMethod i_notificationMethod
, const String
& i_actionComment
)
251 :m_notificationMethod( NULL
)
252 ,m_altNotificationMethod( i_notificationMethod
)
253 ,m_sActionComment( i_actionComment
)
259 return ( m_notificationMethod
!= 0 ) || ( m_altNotificationMethod
!= 0 );
262 void operator()( SfxUndoListener
* i_listener
) const
264 OSL_PRECOND( is(), "NotifyUndoListener: this will crash!" );
265 if ( m_altNotificationMethod
!= 0 )
267 ( i_listener
->*m_altNotificationMethod
)( m_sActionComment
);
271 ( i_listener
->*m_notificationMethod
)();
276 UndoListenerVoidMethod m_notificationMethod
;
277 UndoListenerStringMethod m_altNotificationMethod
;
278 String m_sActionComment
;
281 //--------------------------------------------------------------------
282 class SVL_DLLPRIVATE UndoManagerGuard
285 UndoManagerGuard( SfxUndoManager_Data
& i_managerData
)
286 :m_rManagerData( i_managerData
)
287 ,m_aGuard( i_managerData
.aMutex
)
304 void cancelNotifications()
309 /** marks the given Undo action for deletion
311 The Undo action will be put into a list, whose members will be deleted from within the destructor of the
312 UndoManagerGuard. This deletion will happen without the UndoManager's mutex locked.
314 void markForDeletion( SfxUndoAction
* i_action
)
318 m_aUndoActionsCleanup
.push_back( i_action
);
321 /** schedules the given SfxUndoListener method to be called for all registered listeners.
323 The notification will happen after the Undo manager's mutex has been released, and after all pending
324 deletions of Undo actions are done.
326 void scheduleNotification( UndoListenerVoidMethod i_notificationMethod
)
328 m_notifiers
.push_back( NotifyUndoListener( i_notificationMethod
) );
331 void scheduleNotification( UndoListenerStringMethod i_notificationMethod
, const String
& i_actionComment
)
333 m_notifiers
.push_back( NotifyUndoListener( i_notificationMethod
, i_actionComment
) );
337 SfxUndoManager_Data
& m_rManagerData
;
338 ::osl::ResettableMutexGuard m_aGuard
;
339 ::std::list
< SfxUndoAction
* > m_aUndoActionsCleanup
;
340 ::std::list
< NotifyUndoListener
> m_notifiers
;
343 UndoManagerGuard::~UndoManagerGuard()
346 UndoListeners
aListenersCopy( m_rManagerData
.aListeners
);
351 // delete all actions
352 while ( !m_aUndoActionsCleanup
.empty() )
354 SfxUndoAction
* pAction
= m_aUndoActionsCleanup
.front();
355 m_aUndoActionsCleanup
.pop_front();
360 catch( const Exception
& )
362 DBG_UNHANDLED_EXCEPTION();
366 // handle scheduled notification
367 for ( ::std::list
< NotifyUndoListener
>::const_iterator notifier
= m_notifiers
.begin();
368 notifier
!= m_notifiers
.end();
372 if ( notifier
->is() )
373 ::std::for_each( aListenersCopy
.begin(), aListenersCopy
.end(), *notifier
);
378 using namespace ::svl::undo::impl
;
380 //========================================================================
382 SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount
)
383 :m_pData( new SfxUndoManager_Data( nMaxUndoActionCount
) )
387 //------------------------------------------------------------------------
389 SfxUndoManager::~SfxUndoManager()
391 UndoListeners aListenersCopy
;
393 UndoManagerGuard
aGuard( *m_pData
);
394 aListenersCopy
= m_pData
->aListeners
;
397 ::std::for_each( aListenersCopy
.begin(), aListenersCopy
.end(),
398 NotifyUndoListener( &SfxUndoListener::undoManagerDying
) );
401 //------------------------------------------------------------------------
403 void SfxUndoManager::EnableUndo( bool i_enable
)
405 UndoManagerGuard
aGuard( *m_pData
);
406 ImplEnableUndo_Lock( i_enable
);
410 //------------------------------------------------------------------------
412 void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable
)
414 if ( m_pData
->mbUndoEnabled
== i_enable
)
416 m_pData
->mbUndoEnabled
= i_enable
;
419 //------------------------------------------------------------------------
421 bool SfxUndoManager::IsUndoEnabled() const
423 UndoManagerGuard
aGuard( *m_pData
);
424 return ImplIsUndoEnabled_Lock();
427 //------------------------------------------------------------------------
429 bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
431 return m_pData
->mbUndoEnabled
;
434 //------------------------------------------------------------------------
436 void SfxUndoManager::SetMaxUndoActionCount( size_t nMaxUndoActionCount
)
438 UndoManagerGuard
aGuard( *m_pData
);
440 // Remove entries from the pActUndoArray when we have to reduce
441 // the number of entries due to a lower nMaxUndoActionCount.
442 // Both redo and undo action entries will be removed until we reached the
443 // new nMaxUndoActionCount.
445 long nNumToDelete
= m_pData
->pActUndoArray
->aUndoActions
.size() - nMaxUndoActionCount
;
446 while ( nNumToDelete
> 0 )
448 size_t nPos
= m_pData
->pActUndoArray
->aUndoActions
.size();
449 if ( nPos
> m_pData
->pActUndoArray
->nCurUndoAction
)
451 SfxUndoAction
* pAction
= m_pData
->pActUndoArray
->aUndoActions
[nPos
-1].pAction
;
452 if ( !pAction
->IsLinked() )
454 aGuard
.markForDeletion( pAction
);
455 m_pData
->pActUndoArray
->aUndoActions
.Remove( nPos
-1 );
460 if ( nNumToDelete
> 0 && m_pData
->pActUndoArray
->nCurUndoAction
> 0 )
462 SfxUndoAction
* pAction
= m_pData
->pActUndoArray
->aUndoActions
[0].pAction
;
463 if ( !pAction
->IsLinked() )
465 aGuard
.markForDeletion( pAction
);
466 m_pData
->pActUndoArray
->aUndoActions
.Remove(0);
467 --m_pData
->pActUndoArray
->nCurUndoAction
;
472 if ( nPos
== m_pData
->pActUndoArray
->aUndoActions
.size() )
473 break; // Cannot delete more entries
476 m_pData
->pActUndoArray
->nMaxUndoActions
= nMaxUndoActionCount
;
479 //------------------------------------------------------------------------
481 size_t SfxUndoManager::GetMaxUndoActionCount() const
483 UndoManagerGuard
aGuard( *m_pData
);
484 return m_pData
->pActUndoArray
->nMaxUndoActions
;
487 //------------------------------------------------------------------------
489 void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard
& i_guard
)
492 while ( !m_pData
->pActUndoArray
->aUndoActions
.empty() )
494 size_t deletePos
= m_pData
->pActUndoArray
->aUndoActions
.size() - 1;
495 SfxUndoAction
* pAction
= m_pData
->pActUndoArray
->aUndoActions
[ deletePos
].pAction
;
496 i_guard
.markForDeletion( pAction
);
497 m_pData
->pActUndoArray
->aUndoActions
.Remove( deletePos
);
500 m_pData
->pActUndoArray
->nCurUndoAction
= 0;
502 m_pData
->mnMarks
= 0;
503 m_pData
->mnEmptyMark
= MARK_INVALID
;
506 //------------------------------------------------------------------------
508 void SfxUndoManager::Clear()
510 UndoManagerGuard
aGuard( *m_pData
);
512 OSL_ENSURE( !ImplIsInListAction_Lock(), "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
513 ImplClearCurrentLevel_NoNotify( aGuard
);
516 aGuard
.scheduleNotification( &SfxUndoListener::cleared
);
519 //------------------------------------------------------------------------
521 void SfxUndoManager::ClearAllLevels()
523 UndoManagerGuard
aGuard( *m_pData
);
524 ImplClearCurrentLevel_NoNotify( aGuard
);
526 if ( ImplIsInListAction_Lock() )
528 m_pData
->mbClearUntilTopLevel
= true;
532 aGuard
.scheduleNotification( &SfxUndoListener::cleared
);
536 //------------------------------------------------------------------------
538 void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel
)
540 UndoManagerGuard
aGuard( *m_pData
);
541 ImplClearRedo( aGuard
, i_currentLevel
);
544 //------------------------------------------------------------------------
546 void SfxUndoManager::ClearRedo()
548 OSL_ENSURE( !IsInListAction(), "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
549 ImplClearRedo_NoLock( CurrentLevel
);
552 //------------------------------------------------------------------------
554 void SfxUndoManager::Reset()
556 UndoManagerGuard
aGuard( *m_pData
);
559 while ( !ImplIsUndoEnabled_Lock() )
560 ImplEnableUndo_Lock( true );
562 // cancel all list actions
563 while ( IsInListAction() )
564 ImplLeaveListAction( false, aGuard
);
567 ImplClearCurrentLevel_NoNotify( aGuard
);
569 // cancel the notifications scheduled by ImplLeaveListAction,
570 // as we want to do an own, dedicated notification
571 aGuard
.cancelNotifications();
573 // schedule notification
574 aGuard
.scheduleNotification( &SfxUndoListener::resetAll
);
577 //------------------------------------------------------------------------
579 void SfxUndoManager::ImplClearUndo( UndoManagerGuard
& i_guard
)
581 while ( m_pData
->pActUndoArray
->nCurUndoAction
> 0 )
583 SfxUndoAction
* pUndoAction
= m_pData
->pActUndoArray
->aUndoActions
[0].pAction
;
584 m_pData
->pActUndoArray
->aUndoActions
.Remove( 0 );
585 i_guard
.markForDeletion( pUndoAction
);
586 --m_pData
->pActUndoArray
->nCurUndoAction
;
588 // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
591 //------------------------------------------------------------------------
593 void SfxUndoManager::ImplClearRedo( UndoManagerGuard
& i_guard
, bool const i_currentLevel
)
595 SfxUndoArray
* pUndoArray
= ( i_currentLevel
== IUndoManager::CurrentLevel
) ? m_pData
->pActUndoArray
: m_pData
->pUndoArray
;
598 while ( pUndoArray
->aUndoActions
.size() > pUndoArray
->nCurUndoAction
)
600 size_t deletePos
= pUndoArray
->aUndoActions
.size() - 1;
601 SfxUndoAction
* pAction
= pUndoArray
->aUndoActions
[ deletePos
].pAction
;
602 pUndoArray
->aUndoActions
.Remove( deletePos
);
603 i_guard
.markForDeletion( pAction
);
606 // notification - only if the top level's stack was cleared
607 if ( i_currentLevel
== IUndoManager::TopLevel
)
608 i_guard
.scheduleNotification( &SfxUndoListener::clearedRedo
);
611 //------------------------------------------------------------------------
613 bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction
*pAction
, bool bTryMerge
, bool bClearRedo
, UndoManagerGuard
& i_guard
)
615 if ( !ImplIsUndoEnabled_Lock() || ( m_pData
->pActUndoArray
->nMaxUndoActions
== 0 ) )
617 i_guard
.markForDeletion( pAction
);
621 // merge, if required
622 SfxUndoAction
* pMergeWithAction
= m_pData
->pActUndoArray
->nCurUndoAction
?
623 m_pData
->pActUndoArray
->aUndoActions
[m_pData
->pActUndoArray
->nCurUndoAction
-1].pAction
: NULL
;
624 if ( bTryMerge
&& pMergeWithAction
)
626 bool bMerged
= pMergeWithAction
->Merge( pAction
);
629 i_guard
.markForDeletion( pAction
);
634 // clear redo stack, if requested
635 if ( bClearRedo
&& ( ImplGetRedoActionCount_Lock( CurrentLevel
) > 0 ) )
636 ImplClearRedo( i_guard
, IUndoManager::CurrentLevel
);
638 // respect max number
639 if( m_pData
->pActUndoArray
== m_pData
->pUndoArray
)
641 while( m_pData
->pActUndoArray
->aUndoActions
.size() >=
642 m_pData
->pActUndoArray
->nMaxUndoActions
&&
643 !m_pData
->pActUndoArray
->aUndoActions
[0].pAction
->IsLinked() )
645 i_guard
.markForDeletion( m_pData
->pActUndoArray
->aUndoActions
[0].pAction
);
646 m_pData
->pActUndoArray
->aUndoActions
.Remove(0);
647 if (m_pData
->pActUndoArray
->nCurUndoAction
> 0)
649 --m_pData
->pActUndoArray
->nCurUndoAction
;
651 // fdo#66071 invalidate the current empty mark when removing
652 --m_pData
->mnEmptyMark
;
657 m_pData
->pActUndoArray
->aUndoActions
.Insert( pAction
, m_pData
->pActUndoArray
->nCurUndoAction
++ );
661 //------------------------------------------------------------------------
663 void SfxUndoManager::AddUndoAction( SfxUndoAction
*pAction
, sal_Bool bTryMerge
)
665 UndoManagerGuard
aGuard( *m_pData
);
668 if ( ImplAddUndoAction_NoNotify( pAction
, bTryMerge
, true, aGuard
) )
671 aGuard
.scheduleNotification( &SfxUndoListener::undoActionAdded
, pAction
->GetComment() );
675 //------------------------------------------------------------------------
677 size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel
) const
679 UndoManagerGuard
aGuard( *m_pData
);
680 const SfxUndoArray
* pUndoArray
= i_currentLevel
? m_pData
->pActUndoArray
: m_pData
->pUndoArray
;
681 return pUndoArray
->nCurUndoAction
;
684 //------------------------------------------------------------------------
686 OUString
SfxUndoManager::GetUndoActionComment( size_t nNo
, bool const i_currentLevel
) const
688 UndoManagerGuard
aGuard( *m_pData
);
691 const SfxUndoArray
* pUndoArray
= i_currentLevel
? m_pData
->pActUndoArray
: m_pData
->pUndoArray
;
692 DBG_ASSERT( nNo
< pUndoArray
->nCurUndoAction
, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" );
693 if( nNo
< pUndoArray
->nCurUndoAction
)
694 sComment
= pUndoArray
->aUndoActions
[ pUndoArray
->nCurUndoAction
- 1 - nNo
].pAction
->GetComment();
698 //------------------------------------------------------------------------
700 sal_uInt16
SfxUndoManager::GetUndoActionId() const
702 UndoManagerGuard
aGuard( *m_pData
);
704 DBG_ASSERT( m_pData
->pActUndoArray
->nCurUndoAction
> 0, "svl::SfxUndoManager::GetUndoActionId(), illegal id!" );
705 if ( m_pData
->pActUndoArray
->nCurUndoAction
== 0 )
707 return m_pData
->pActUndoArray
->aUndoActions
[m_pData
->pActUndoArray
->nCurUndoAction
-1].pAction
->GetId();
710 //------------------------------------------------------------------------
712 SfxUndoAction
* SfxUndoManager::GetUndoAction( size_t nNo
) const
714 UndoManagerGuard
aGuard( *m_pData
);
716 DBG_ASSERT( nNo
< m_pData
->pActUndoArray
->nCurUndoAction
, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
717 if( nNo
>= m_pData
->pActUndoArray
->nCurUndoAction
)
719 return m_pData
->pActUndoArray
->aUndoActions
[m_pData
->pActUndoArray
->nCurUndoAction
-1-nNo
].pAction
;
722 //------------------------------------------------------------------------
724 /** clears the redo stack and removes the top undo action */
725 void SfxUndoManager::RemoveLastUndoAction()
727 UndoManagerGuard
aGuard( *m_pData
);
729 ENSURE_OR_RETURN_VOID( m_pData
->pActUndoArray
->nCurUndoAction
, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
731 m_pData
->pActUndoArray
->nCurUndoAction
--;
733 // delete redo-actions and top action
734 for ( size_t nPos
= m_pData
->pActUndoArray
->aUndoActions
.size(); nPos
> m_pData
->pActUndoArray
->nCurUndoAction
; --nPos
)
736 aGuard
.markForDeletion( m_pData
->pActUndoArray
->aUndoActions
[nPos
-1].pAction
);
739 m_pData
->pActUndoArray
->aUndoActions
.Remove(
740 m_pData
->pActUndoArray
->nCurUndoAction
,
741 m_pData
->pActUndoArray
->aUndoActions
.size() - m_pData
->pActUndoArray
->nCurUndoAction
);
744 //------------------------------------------------------------------------
746 bool SfxUndoManager::IsDoing() const
748 UndoManagerGuard
aGuard( *m_pData
);
749 return m_pData
->mbDoing
;
752 //------------------------------------------------------------------------
754 sal_Bool
SfxUndoManager::Undo()
756 return ImplUndo( NULL
);
759 //------------------------------------------------------------------------
761 sal_Bool
SfxUndoManager::UndoWithContext( SfxUndoContext
& i_context
)
763 return ImplUndo( &i_context
);
766 //------------------------------------------------------------------------
768 sal_Bool
SfxUndoManager::ImplUndo( SfxUndoContext
* i_contextOrNull
)
770 UndoManagerGuard
aGuard( *m_pData
);
771 OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
773 ::comphelper::FlagGuard
aDoingGuard( m_pData
->mbDoing
);
774 LockGuard
aLockGuard( *this );
776 if ( ImplIsInListAction_Lock() )
778 OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" );
782 if ( m_pData
->pActUndoArray
->nCurUndoAction
== 0 )
784 OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" );
788 SfxUndoAction
* pAction
= m_pData
->pActUndoArray
->aUndoActions
[ --m_pData
->pActUndoArray
->nCurUndoAction
].pAction
;
789 const String sActionComment
= pAction
->GetComment();
792 // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
795 if ( i_contextOrNull
!= NULL
)
796 pAction
->UndoWithContext( *i_contextOrNull
);
805 // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
806 // we still find pAction in our current Undo array
807 size_t nCurAction
= 0;
808 while ( nCurAction
< m_pData
->pActUndoArray
->aUndoActions
.size() )
810 if ( m_pData
->pActUndoArray
->aUndoActions
[ nCurAction
++ ].pAction
== pAction
)
812 // the Undo action is still there ...
813 // assume the error is a permanent failure, and clear the Undo stack
814 ImplClearUndo( aGuard
);
818 OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
822 aGuard
.scheduleNotification( &SfxUndoListener::actionUndone
, sActionComment
);
827 //------------------------------------------------------------------------
829 size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel
) const
831 UndoManagerGuard
aGuard( *m_pData
);
832 return ImplGetRedoActionCount_Lock( i_currentLevel
);
835 //------------------------------------------------------------------------
837 size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel
) const
839 const SfxUndoArray
* pUndoArray
= i_currentLevel
? m_pData
->pActUndoArray
: m_pData
->pUndoArray
;
840 return pUndoArray
->aUndoActions
.size() - pUndoArray
->nCurUndoAction
;
843 //------------------------------------------------------------------------
845 OUString
SfxUndoManager::GetRedoActionComment( size_t nNo
, bool const i_currentLevel
) const
847 UndoManagerGuard
aGuard( *m_pData
);
848 const SfxUndoArray
* pUndoArray
= i_currentLevel
? m_pData
->pActUndoArray
: m_pData
->pUndoArray
;
849 return pUndoArray
->aUndoActions
[ pUndoArray
->nCurUndoAction
+ nNo
].pAction
->GetComment();
852 //------------------------------------------------------------------------
854 sal_Bool
SfxUndoManager::Redo()
856 return ImplRedo( NULL
);
859 //------------------------------------------------------------------------
861 sal_Bool
SfxUndoManager::RedoWithContext( SfxUndoContext
& i_context
)
863 return ImplRedo( &i_context
);
866 //------------------------------------------------------------------------
868 sal_Bool
SfxUndoManager::ImplRedo( SfxUndoContext
* i_contextOrNull
)
870 UndoManagerGuard
aGuard( *m_pData
);
871 OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
873 ::comphelper::FlagGuard
aDoingGuard( m_pData
->mbDoing
);
874 LockGuard
aLockGuard( *this );
876 if ( ImplIsInListAction_Lock() )
878 OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
882 if ( m_pData
->pActUndoArray
->nCurUndoAction
>= m_pData
->pActUndoArray
->aUndoActions
.size() )
884 OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" );
888 SfxUndoAction
* pAction
= m_pData
->pActUndoArray
->aUndoActions
[ m_pData
->pActUndoArray
->nCurUndoAction
++ ].pAction
;
889 const String sActionComment
= pAction
->GetComment();
892 // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
895 if ( i_contextOrNull
!= NULL
)
896 pAction
->RedoWithContext( *i_contextOrNull
);
905 // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
906 // we still find pAction in our current Undo array
907 size_t nCurAction
= 0;
908 while ( nCurAction
< m_pData
->pActUndoArray
->aUndoActions
.size() )
910 if ( m_pData
->pActUndoArray
->aUndoActions
[ nCurAction
].pAction
== pAction
)
912 // the Undo action is still there ...
913 // assume the error is a permanent failure, and clear the Undo stack
914 ImplClearRedo( aGuard
, IUndoManager::CurrentLevel
);
919 OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
923 aGuard
.scheduleNotification( &SfxUndoListener::actionRedone
, sActionComment
);
928 //------------------------------------------------------------------------
930 size_t SfxUndoManager::GetRepeatActionCount() const
932 UndoManagerGuard
aGuard( *m_pData
);
933 return m_pData
->pActUndoArray
->aUndoActions
.size();
936 //------------------------------------------------------------------------
938 OUString
SfxUndoManager::GetRepeatActionComment(SfxRepeatTarget
&rTarget
) const
940 UndoManagerGuard
aGuard( *m_pData
);
941 return m_pData
->pActUndoArray
->aUndoActions
[ m_pData
->pActUndoArray
->aUndoActions
.size() - 1 ].pAction
942 ->GetRepeatComment(rTarget
);
945 //------------------------------------------------------------------------
947 sal_Bool
SfxUndoManager::Repeat( SfxRepeatTarget
&rTarget
)
949 UndoManagerGuard
aGuard( *m_pData
);
950 if ( !m_pData
->pActUndoArray
->aUndoActions
.empty() )
952 SfxUndoAction
* pAction
= m_pData
->pActUndoArray
->aUndoActions
[ m_pData
->pActUndoArray
->aUndoActions
.size() - 1 ].pAction
;
954 if ( pAction
->CanRepeat( rTarget
) )
955 pAction
->Repeat( rTarget
);
962 //------------------------------------------------------------------------
964 sal_Bool
SfxUndoManager::CanRepeat( SfxRepeatTarget
&rTarget
) const
966 UndoManagerGuard
aGuard( *m_pData
);
967 if ( !m_pData
->pActUndoArray
->aUndoActions
.empty() )
969 size_t nActionNo
= m_pData
->pActUndoArray
->aUndoActions
.size() - 1;
970 return m_pData
->pActUndoArray
->aUndoActions
[nActionNo
].pAction
->CanRepeat(rTarget
);
975 //------------------------------------------------------------------------
977 void SfxUndoManager::AddUndoListener( SfxUndoListener
& i_listener
)
979 UndoManagerGuard
aGuard( *m_pData
);
980 m_pData
->aListeners
.push_back( &i_listener
);
983 //------------------------------------------------------------------------
985 void SfxUndoManager::RemoveUndoListener( SfxUndoListener
& i_listener
)
987 UndoManagerGuard
aGuard( *m_pData
);
988 for ( UndoListeners::iterator lookup
= m_pData
->aListeners
.begin();
989 lookup
!= m_pData
->aListeners
.end();
993 if ( (*lookup
) == &i_listener
)
995 m_pData
->aListeners
.erase( lookup
);
1001 //------------------------------------------------------------------------
1003 void SfxUndoManager::EnterListAction(
1004 const OUString
& rComment
, const OUString
&rRepeatComment
, sal_uInt16 nId
)
1008 Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles.
1012 UndoManagerGuard
aGuard( *m_pData
);
1014 if( !ImplIsUndoEnabled_Lock() )
1017 if ( !m_pData
->pUndoArray
->nMaxUndoActions
)
1020 m_pData
->pFatherUndoArray
= m_pData
->pActUndoArray
;
1021 SfxListUndoAction
* pAction
= new SfxListUndoAction( rComment
, rRepeatComment
, nId
, m_pData
->pActUndoArray
);
1022 OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction
, false, false, aGuard
) );
1023 // expected to succeed: all conditions under which it could fail should have been checked already
1024 m_pData
->pActUndoArray
= pAction
;
1027 aGuard
.scheduleNotification( &SfxUndoListener::listActionEntered
, rComment
);
1030 //------------------------------------------------------------------------
1032 bool SfxUndoManager::IsInListAction() const
1034 UndoManagerGuard
aGuard( *m_pData
);
1035 return ImplIsInListAction_Lock();
1038 //------------------------------------------------------------------------
1040 bool SfxUndoManager::ImplIsInListAction_Lock() const
1042 return ( m_pData
->pActUndoArray
!= m_pData
->pUndoArray
);
1045 //------------------------------------------------------------------------
1047 size_t SfxUndoManager::GetListActionDepth() const
1049 UndoManagerGuard
aGuard( *m_pData
);
1052 SfxUndoArray
* pLookup( m_pData
->pActUndoArray
);
1053 while ( pLookup
!= m_pData
->pUndoArray
)
1055 pLookup
= pLookup
->pFatherUndoArray
;
1062 //------------------------------------------------------------------------
1064 size_t SfxUndoManager::LeaveListAction()
1066 UndoManagerGuard
aGuard( *m_pData
);
1067 size_t nCount
= ImplLeaveListAction( false, aGuard
);
1069 if ( m_pData
->mbClearUntilTopLevel
)
1071 ImplClearCurrentLevel_NoNotify( aGuard
);
1072 if ( !ImplIsInListAction_Lock() )
1074 m_pData
->mbClearUntilTopLevel
= false;
1075 aGuard
.scheduleNotification( &SfxUndoListener::cleared
);
1083 //------------------------------------------------------------------------
1085 size_t SfxUndoManager::LeaveAndMergeListAction()
1087 UndoManagerGuard
aGuard( *m_pData
);
1088 return ImplLeaveListAction( true, aGuard
);
1091 //------------------------------------------------------------------------
1093 size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge
, UndoManagerGuard
& i_guard
)
1095 if ( !ImplIsUndoEnabled_Lock() )
1098 if ( !m_pData
->pUndoArray
->nMaxUndoActions
)
1101 if( !ImplIsInListAction_Lock() )
1103 OSL_TRACE( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
1107 DBG_ASSERT( m_pData
->pActUndoArray
->pFatherUndoArray
, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" );
1109 // the array/level which we're about to leave
1110 SfxUndoArray
* pArrayToLeave
= m_pData
->pActUndoArray
;
1112 m_pData
->pActUndoArray
= m_pData
->pActUndoArray
->pFatherUndoArray
;
1114 // If no undo actions were added to the list, delete the list action
1115 const size_t nListActionElements
= pArrayToLeave
->nCurUndoAction
;
1116 if ( nListActionElements
== 0 )
1118 SfxUndoAction
* pCurrentAction
= m_pData
->pActUndoArray
->aUndoActions
[ m_pData
->pActUndoArray
->nCurUndoAction
-1 ].pAction
;
1119 m_pData
->pActUndoArray
->aUndoActions
.Remove( --m_pData
->pActUndoArray
->nCurUndoAction
);
1120 i_guard
.markForDeletion( pCurrentAction
);
1122 i_guard
.scheduleNotification( &SfxUndoListener::listActionCancelled
);
1126 // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1128 ImplClearRedo( i_guard
, IUndoManager::CurrentLevel
);
1130 SfxUndoAction
* pCurrentAction
= m_pData
->pActUndoArray
->aUndoActions
[ m_pData
->pActUndoArray
->nCurUndoAction
-1 ].pAction
;
1131 SfxListUndoAction
* pListAction
= dynamic_cast< SfxListUndoAction
* >( pCurrentAction
);
1132 ENSURE_OR_RETURN( pListAction
, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements
);
1136 // merge the list action with its predecessor on the same level
1137 OSL_ENSURE( m_pData
->pActUndoArray
->nCurUndoAction
> 1,
1138 "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
1139 if ( m_pData
->pActUndoArray
->nCurUndoAction
> 1 )
1141 SfxUndoAction
* pPreviousAction
= m_pData
->pActUndoArray
->aUndoActions
[ m_pData
->pActUndoArray
->nCurUndoAction
- 2 ].pAction
;
1142 m_pData
->pActUndoArray
->aUndoActions
.Remove( m_pData
->pActUndoArray
->nCurUndoAction
- 2 );
1143 --m_pData
->pActUndoArray
->nCurUndoAction
;
1144 pListAction
->aUndoActions
.Insert( pPreviousAction
, 0 );
1145 ++pListAction
->nCurUndoAction
;
1147 pListAction
->SetComment( pPreviousAction
->GetComment() );
1151 // if the undo array has no comment, try to get it from its children
1152 if ( pListAction
->GetComment().isEmpty() )
1154 for( size_t n
= 0; n
< pListAction
->aUndoActions
.size(); n
++ )
1156 if (!pListAction
->aUndoActions
[n
].pAction
->GetComment().isEmpty())
1158 pListAction
->SetComment( pListAction
->aUndoActions
[n
].pAction
->GetComment() );
1165 i_guard
.scheduleNotification( &SfxUndoListener::listActionLeft
, pListAction
->GetComment() );
1168 return nListActionElements
;
1171 //------------------------------------------------------------------------
1172 UndoStackMark
SfxUndoManager::MarkTopUndoAction()
1174 UndoManagerGuard
aGuard( *m_pData
);
1176 OSL_ENSURE( !IsInListAction(),
1177 "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
1178 OSL_ENSURE((m_pData
->mnMarks
+ 1) < (m_pData
->mnEmptyMark
- 1),
1179 "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
1181 size_t const nActionPos
= m_pData
->pUndoArray
->nCurUndoAction
;
1182 if (0 == nActionPos
)
1184 --m_pData
->mnEmptyMark
;
1185 return m_pData
->mnEmptyMark
;
1188 m_pData
->pUndoArray
->aUndoActions
[ nActionPos
-1 ].aMarks
.push_back(
1189 ++m_pData
->mnMarks
);
1190 return m_pData
->mnMarks
;
1193 //------------------------------------------------------------------------
1194 void SfxUndoManager::RemoveMark( UndoStackMark
const i_mark
)
1196 UndoManagerGuard
aGuard( *m_pData
);
1198 if ((m_pData
->mnEmptyMark
< i_mark
) || (MARK_INVALID
== i_mark
))
1200 return; // nothing to remove
1202 else if (i_mark
== m_pData
->mnEmptyMark
)
1204 --m_pData
->mnEmptyMark
; // never returned from MarkTop => invalid
1208 for ( size_t i
=0; i
<m_pData
->pUndoArray
->aUndoActions
.size(); ++i
)
1210 MarkedUndoAction
& rAction
= m_pData
->pUndoArray
->aUndoActions
[i
];
1211 for ( ::std::vector
< UndoStackMark
>::iterator markPos
= rAction
.aMarks
.begin();
1212 markPos
!= rAction
.aMarks
.end();
1216 if ( *markPos
== i_mark
)
1218 rAction
.aMarks
.erase( markPos
);
1223 OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" );
1224 // TODO: this might be too offensive. There are situations where we implicitly remove marks
1225 // without our clients, in particular the client which created the mark, having a chance to know
1229 //------------------------------------------------------------------------
1230 bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark
const i_mark
)
1232 UndoManagerGuard
aGuard( *m_pData
);
1234 size_t nActionPos
= m_pData
->pUndoArray
->nCurUndoAction
;
1235 if ( nActionPos
== 0 )
1237 return (i_mark
== m_pData
->mnEmptyMark
);
1240 const MarkedUndoAction
& rAction
=
1241 m_pData
->pUndoArray
->aUndoActions
[ nActionPos
-1 ];
1242 for ( ::std::vector
< UndoStackMark
>::const_iterator markPos
= rAction
.aMarks
.begin();
1243 markPos
!= rAction
.aMarks
.end();
1247 if ( *markPos
== i_mark
)
1254 //------------------------------------------------------------------------
1256 void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count
)
1258 UndoManagerGuard
aGuard( *m_pData
);
1260 size_t nActionsToRemove
= i_count
;
1261 while ( nActionsToRemove
)
1263 SfxUndoAction
* pActionToRemove
= m_pData
->pUndoArray
->aUndoActions
[0].pAction
;
1265 if ( IsInListAction() && ( m_pData
->pUndoArray
->nCurUndoAction
== 1 ) )
1267 OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" );
1271 aGuard
.markForDeletion( pActionToRemove
);
1272 m_pData
->pUndoArray
->aUndoActions
.Remove( 0 );
1273 --m_pData
->pUndoArray
->nCurUndoAction
;
1278 //------------------------------------------------------------------------
1280 sal_uInt16
SfxListUndoAction::GetId() const
1285 //------------------------------------------------------------------------
1287 OUString
SfxListUndoAction::GetComment() const
1292 //------------------------------------------------------------------------
1294 void SfxListUndoAction::SetComment(const OUString
& rComment
)
1296 aComment
= rComment
;
1299 //------------------------------------------------------------------------
1301 OUString
SfxListUndoAction::GetRepeatComment(SfxRepeatTarget
&) const
1303 return aRepeatComment
;
1307 //------------------------------------------------------------------------
1309 SfxListUndoAction::SfxListUndoAction
1311 const OUString
&rComment
,
1312 const OUString rRepeatComment
,
1314 SfxUndoArray
*pFather
1316 : nId(Id
), aComment(rComment
), aRepeatComment(rRepeatComment
)
1318 pFatherUndoArray
= pFather
;
1319 nMaxUndoActions
= USHRT_MAX
;
1322 //------------------------------------------------------------------------
1324 void SfxListUndoAction::Undo()
1326 for(size_t i
=nCurUndoAction
;i
>0;)
1327 aUndoActions
[--i
].pAction
->Undo();
1331 //------------------------------------------------------------------------
1333 void SfxListUndoAction::UndoWithContext( SfxUndoContext
& i_context
)
1335 for(size_t i
=nCurUndoAction
;i
>0;)
1336 aUndoActions
[--i
].pAction
->UndoWithContext( i_context
);
1340 //------------------------------------------------------------------------
1342 void SfxListUndoAction::Redo()
1344 for(size_t i
=nCurUndoAction
;i
<aUndoActions
.size();i
++)
1345 aUndoActions
[i
].pAction
->Redo();
1346 nCurUndoAction
= aUndoActions
.size();
1349 //------------------------------------------------------------------------
1351 void SfxListUndoAction::RedoWithContext( SfxUndoContext
& i_context
)
1353 for(size_t i
=nCurUndoAction
;i
<aUndoActions
.size();i
++)
1354 aUndoActions
[i
].pAction
->RedoWithContext( i_context
);
1355 nCurUndoAction
= aUndoActions
.size();
1358 //------------------------------------------------------------------------
1360 void SfxListUndoAction::Repeat(SfxRepeatTarget
&rTarget
)
1362 for(size_t i
=0;i
<nCurUndoAction
;i
++)
1363 aUndoActions
[i
].pAction
->Repeat(rTarget
);
1366 //------------------------------------------------------------------------
1368 sal_Bool
SfxListUndoAction::CanRepeat(SfxRepeatTarget
&r
) const
1370 for(size_t i
=0;i
<nCurUndoAction
;i
++)
1371 if(!aUndoActions
[i
].pAction
->CanRepeat(r
))
1376 //------------------------------------------------------------------------
1378 sal_Bool
SfxListUndoAction::Merge( SfxUndoAction
*pNextAction
)
1380 return !aUndoActions
.empty() && aUndoActions
[aUndoActions
.size()-1].pAction
->Merge( pNextAction
);
1383 //------------------------------------------------------------------------
1385 SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager
*pManager
)
1388 Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt.
1389 Holt sich als zugehoerige Action des weiteren UndoManagers dessen
1394 pUndoManager
= pManager
;
1395 SfxUndoManager
* pUndoManagerImplementation
= dynamic_cast< SfxUndoManager
* >( pManager
);
1396 ENSURE_OR_THROW( pUndoManagerImplementation
!= NULL
, "unsupported undo manager implementation!" );
1397 // yes, this cast is dirty. But reaching into the SfxUndoManager's implementation,
1398 // directly accessing its internal stack, and tampering with an action on that stack
1400 if ( pManager
->GetMaxUndoActionCount() )
1402 size_t nPos
= pManager
->GetUndoActionCount()-1;
1403 pAction
= pUndoManagerImplementation
->m_pData
->pActUndoArray
->aUndoActions
[nPos
].pAction
;
1404 pAction
->SetLinked();
1410 //------------------------------------------------------------------------
1412 void SfxLinkUndoAction::Undo()
1415 pUndoManager
->Undo();
1418 //------------------------------------------------------------------------
1420 void SfxLinkUndoAction::Redo()
1423 pUndoManager
->Redo();
1426 //------------------------------------------------------------------------
1429 sal_Bool
SfxLinkUndoAction::CanRepeat(SfxRepeatTarget
& r
) const
1431 return pAction
&& pAction
->CanRepeat(r
);
1435 //------------------------------------------------------------------------
1438 void SfxLinkUndoAction::Repeat(SfxRepeatTarget
&r
)
1440 if ( pAction
&& pAction
->CanRepeat( r
) )
1441 pAction
->Repeat( r
);
1445 //------------------------------------------------------------------------
1447 OUString
SfxLinkUndoAction::GetComment() const
1450 return pAction
->GetComment();
1455 //------------------------------------------------------------------------
1457 OUString
SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget
&r
) const
1460 return pAction
->GetRepeatComment(r
);
1464 //------------------------------------------------------------------------
1466 SfxLinkUndoAction::~SfxLinkUndoAction()
1469 pAction
->SetLinked( sal_False
);
1473 //------------------------------------------------------------------------
1475 SfxUndoArray::~SfxUndoArray()
1477 while ( !aUndoActions
.empty() )
1479 SfxUndoAction
*pAction
= aUndoActions
[ aUndoActions
.size() - 1 ].pAction
;
1480 aUndoActions
.Remove( aUndoActions
.size() - 1 );
1486 sal_uInt16
SfxLinkUndoAction::GetId() const
1488 return pAction
? pAction
->GetId() : 0;
1491 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */