bump product version to 4.1.6.2
[LibreOffice.git] / svl / source / undo / undo.cxx
blobd745751fa7285519e16e39b048fed9470f26d85c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
29 #include <vector>
30 #include <list>
31 #include <limits>
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()
62 return bLinked;
65 //------------------------------------------------------------------------
67 void SfxUndoAction::SetLinked( sal_Bool bIsLinked )
69 bLinked = 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);
92 return sal_False;
95 //------------------------------------------------------------------------
97 OUString SfxUndoAction::GetComment() const
99 DBG_CHKTHIS(SfxUndoAction, 0);
100 return OUString();
103 //------------------------------------------------------------------------
106 sal_uInt16 SfxUndoAction::GetId() const
108 DBG_CHKTHIS(SfxUndoAction, 0);
109 return 0;
112 //------------------------------------------------------------------------
114 OUString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
116 DBG_CHKTHIS(SfxUndoAction, 0);
117 return GetComment();
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 )
132 (void)i_context;
133 Undo();
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 )
148 (void)i_context;
149 Redo();
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
165 return sal_True;
168 //========================================================================
170 typedef ::std::vector< SfxUndoListener* > UndoListeners;
172 struct SVL_DLLPRIVATE SfxUndoManager_Data
174 ::osl::Mutex aMutex;
175 SfxUndoArray* pUndoArray;
176 SfxUndoArray* pActUndoArray;
177 SfxUndoArray* pFatherUndoArray;
179 sal_Int32 mnMarks;
180 sal_Int32 mnEmptyMark;
181 bool mbUndoEnabled;
182 bool mbDoing;
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 )
191 ,mnMarks( 0 )
192 ,mnEmptyMark(MARK_INVALID)
193 ,mbUndoEnabled( true )
194 ,mbDoing( false )
195 ,mbClearUntilTopLevel( false )
197 pActUndoArray = pUndoArray;
200 ~SfxUndoManager_Data()
202 delete pUndoArray;
206 //========================================================================
208 namespace svl { namespace undo { namespace impl
210 //--------------------------------------------------------------------
211 class SVL_DLLPRIVATE LockGuard
213 public:
214 LockGuard( SfxUndoManager& i_manager )
215 :m_manager( i_manager )
217 m_manager.ImplEnableUndo_Lock( false );
220 ~LockGuard()
222 m_manager.ImplEnableUndo_Lock( true );
225 private:
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 >
236 NotifyUndoListener()
237 :m_notificationMethod( NULL )
238 ,m_altNotificationMethod( NULL )
239 ,m_sActionComment()
243 NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
244 :m_notificationMethod( i_notificationMethod )
245 ,m_altNotificationMethod( NULL )
246 ,m_sActionComment()
250 NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const String& i_actionComment )
251 :m_notificationMethod( NULL )
252 ,m_altNotificationMethod( i_notificationMethod )
253 ,m_sActionComment( i_actionComment )
257 bool is() const
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 );
269 else
271 ( i_listener->*m_notificationMethod )();
275 private:
276 UndoListenerVoidMethod m_notificationMethod;
277 UndoListenerStringMethod m_altNotificationMethod;
278 String m_sActionComment;
281 //--------------------------------------------------------------------
282 class SVL_DLLPRIVATE UndoManagerGuard
284 public:
285 UndoManagerGuard( SfxUndoManager_Data& i_managerData )
286 :m_rManagerData( i_managerData )
287 ,m_aGuard( i_managerData.aMutex )
288 ,m_notifiers()
292 ~UndoManagerGuard();
294 void clear()
296 m_aGuard.clear();
299 void reset()
301 m_aGuard.reset();
304 void cancelNotifications()
306 m_notifiers.clear();
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 )
316 // remember
317 if ( 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 ) );
336 private:
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()
345 // copy members
346 UndoListeners aListenersCopy( m_rManagerData.aListeners );
348 // release mutex
349 m_aGuard.clear();
351 // delete all actions
352 while ( !m_aUndoActionsCleanup.empty() )
354 SfxUndoAction* pAction = m_aUndoActionsCleanup.front();
355 m_aUndoActionsCleanup.pop_front();
358 delete pAction;
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();
369 ++notifier
372 if ( notifier->is() )
373 ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), *notifier );
376 } } }
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 )
415 return;
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 );
456 --nNumToDelete;
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;
468 --nNumToDelete;
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 )
491 // clear array
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 );
515 // notify listeners
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;
530 else
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 );
558 // clear all locks
559 while ( !ImplIsUndoEnabled_Lock() )
560 ImplEnableUndo_Lock( true );
562 // cancel all list actions
563 while ( IsInListAction() )
564 ImplLeaveListAction( false, aGuard );
566 // clear both stacks
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;
597 // clearance
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 );
618 return false;
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 );
627 if ( bMerged )
629 i_guard.markForDeletion( pAction );
630 return false;
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;
656 // append new action
657 m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ );
658 return true;
661 //------------------------------------------------------------------------
663 void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerge )
665 UndoManagerGuard aGuard( *m_pData );
667 // add
668 if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
670 // notify listeners
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 );
690 OUString sComment;
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();
695 return sComment;
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 )
706 return 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 )
718 return NULL;
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!" );
779 return sal_False;
782 if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
784 OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" );
785 return sal_False;
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
793 // nowadays ...
794 aGuard.clear();
795 if ( i_contextOrNull != NULL )
796 pAction->UndoWithContext( *i_contextOrNull );
797 else
798 pAction->Undo();
799 aGuard.reset();
801 catch( ... )
803 aGuard.reset();
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 );
815 throw;
818 OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
819 throw;
822 aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
824 return sal_True;
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!" );
879 return sal_False;
882 if ( m_pData->pActUndoArray->nCurUndoAction >= m_pData->pActUndoArray->aUndoActions.size() )
884 OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" );
885 return sal_False;
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
893 // nowadays ...
894 aGuard.clear();
895 if ( i_contextOrNull != NULL )
896 pAction->RedoWithContext( *i_contextOrNull );
897 else
898 pAction->Redo();
899 aGuard.reset();
901 catch( ... )
903 aGuard.reset();
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 );
915 throw;
917 ++nCurAction;
919 OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
920 throw;
923 aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
925 return sal_True;
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;
953 aGuard.clear();
954 if ( pAction->CanRepeat( rTarget ) )
955 pAction->Repeat( rTarget );
956 return sal_True;
959 return sal_False;
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);
972 return sal_False;
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();
990 ++lookup
993 if ( (*lookup) == &i_listener )
995 m_pData->aListeners.erase( lookup );
996 break;
1001 //------------------------------------------------------------------------
1003 void SfxUndoManager::EnterListAction(
1004 const OUString& rComment, const OUString &rRepeatComment, sal_uInt16 nId )
1006 /* [Beschreibung]
1008 Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles.
1012 UndoManagerGuard aGuard( *m_pData );
1014 if( !ImplIsUndoEnabled_Lock() )
1015 return;
1017 if ( !m_pData->pUndoArray->nMaxUndoActions )
1018 return;
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;
1026 // notification
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 );
1050 size_t nDepth(0);
1052 SfxUndoArray* pLookup( m_pData->pActUndoArray );
1053 while ( pLookup != m_pData->pUndoArray )
1055 pLookup = pLookup->pFatherUndoArray;
1056 ++nDepth;
1059 return nDepth;
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 );
1077 nCount = 0;
1080 return nCount;
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() )
1096 return 0;
1098 if ( !m_pData->pUndoArray->nMaxUndoActions )
1099 return 0;
1101 if( !ImplIsInListAction_Lock() )
1103 OSL_TRACE( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
1104 return 0;
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;
1111 // one step up
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 );
1123 return 0;
1126 // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1127 // the redo stack
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 );
1134 if ( i_merge )
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() );
1159 break;
1164 // notify listeners
1165 i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
1167 // outta here
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
1205 return;
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();
1213 ++markPos
1216 if ( *markPos == i_mark )
1218 rAction.aMarks.erase( markPos );
1219 return;
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
1226 // about this.
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();
1244 ++markPos
1247 if ( *markPos == i_mark )
1248 return true;
1251 return false;
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!" );
1268 return;
1271 aGuard.markForDeletion( pActionToRemove );
1272 m_pData->pUndoArray->aUndoActions.Remove( 0 );
1273 --m_pData->pUndoArray->nCurUndoAction;
1274 --nActionsToRemove;
1278 //------------------------------------------------------------------------
1280 sal_uInt16 SfxListUndoAction::GetId() const
1282 return nId;
1285 //------------------------------------------------------------------------
1287 OUString SfxListUndoAction::GetComment() const
1289 return aComment;
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,
1313 sal_uInt16 Id,
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();
1328 nCurUndoAction=0;
1331 //------------------------------------------------------------------------
1333 void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1335 for(size_t i=nCurUndoAction;i>0;)
1336 aUndoActions[--i].pAction->UndoWithContext( i_context );
1337 nCurUndoAction=0;
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))
1372 return sal_False;
1373 return sal_True;
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)
1386 /* [Beschreibung]
1388 Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt.
1389 Holt sich als zugehoerige Action des weiteren UndoManagers dessen
1390 aktuelle Action.
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
1399 // is dirty, too.
1400 if ( pManager->GetMaxUndoActionCount() )
1402 size_t nPos = pManager->GetUndoActionCount()-1;
1403 pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction;
1404 pAction->SetLinked();
1406 else
1407 pAction = 0;
1410 //------------------------------------------------------------------------
1412 void SfxLinkUndoAction::Undo()
1414 if ( pAction )
1415 pUndoManager->Undo();
1418 //------------------------------------------------------------------------
1420 void SfxLinkUndoAction::Redo()
1422 if ( pAction )
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
1449 if ( pAction )
1450 return pAction->GetComment();
1451 return OUString();
1455 //------------------------------------------------------------------------
1457 OUString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
1459 if ( pAction )
1460 return pAction->GetRepeatComment(r);
1461 return OUString();
1464 //------------------------------------------------------------------------
1466 SfxLinkUndoAction::~SfxLinkUndoAction()
1468 if( pAction )
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 );
1481 delete pAction;
1486 sal_uInt16 SfxLinkUndoAction::GetId() const
1488 return pAction ? pAction->GetId() : 0;
1491 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */