xref: /aoo41x/main/svl/source/undo/undo.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svl.hxx"
30 
31 #include <com/sun/star/uno/Exception.hpp>
32 
33 #include <comphelper/flagguard.hxx>
34 #include <tools/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 
37 #include <svl/undo.hxx>
38 
39 #include <vector>
40 #include <list>
41 #include <limits>
42 
43 using ::com::sun::star::uno::Exception;
44 
45 // STATIC DATA -----------------------------------------------------------
46 
47 DBG_NAME(SfxUndoAction)
48 
49 //========================================================================
50 
51 TYPEINIT0(SfxUndoAction);
52 TYPEINIT0(SfxListUndoAction);
53 TYPEINIT0(SfxLinkUndoAction);
54 TYPEINIT0(SfxRepeatTarget);
55 
56 //------------------------------------------------------------------------
57 
58 SfxRepeatTarget::~SfxRepeatTarget()
59 {
60 }
61 
62 //------------------------------------------------------------------------
63 
64 SfxUndoContext::~SfxUndoContext()
65 {
66 }
67 
68 //------------------------------------------------------------------------
69 
70 sal_Bool SfxUndoAction::IsLinked()
71 {
72 	return bLinked;
73 }
74 
75 //------------------------------------------------------------------------
76 
77 void SfxUndoAction::SetLinked( sal_Bool bIsLinked )
78 {
79 	bLinked = bIsLinked;
80 }
81 
82 //------------------------------------------------------------------------
83 
84 SfxUndoAction::~SfxUndoAction()
85 {
86 	DBG_DTOR(SfxUndoAction, 0);
87 	DBG_ASSERT( !IsLinked(), "Gelinkte Action geloescht" );
88 }
89 
90 
91 SfxUndoAction::SfxUndoAction()
92 {
93 	DBG_CTOR(SfxUndoAction, 0);
94 	SetLinked( sal_False );
95 }
96 
97 //------------------------------------------------------------------------
98 
99 sal_Bool SfxUndoAction::Merge( SfxUndoAction * )
100 {
101 	DBG_CHKTHIS(SfxUndoAction, 0);
102 	return sal_False;
103 }
104 
105 //------------------------------------------------------------------------
106 
107 XubString SfxUndoAction::GetComment() const
108 {
109 	DBG_CHKTHIS(SfxUndoAction, 0);
110 	return XubString();
111 }
112 
113 //------------------------------------------------------------------------
114 
115 
116 sal_uInt16 SfxUndoAction::GetId() const
117 {
118 	DBG_CHKTHIS(SfxUndoAction, 0);
119 	return 0;
120 }
121 
122 //------------------------------------------------------------------------
123 
124 XubString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
125 {
126 	DBG_CHKTHIS(SfxUndoAction, 0);
127 	return GetComment();
128 }
129 
130 //------------------------------------------------------------------------
131 
132 void SfxUndoAction::Undo()
133 {
134 	// die sind nur konzeptuell pure virtual
135 	DBG_ERROR( "pure virtual function called: SfxUndoAction::Undo()" );
136 }
137 
138 //------------------------------------------------------------------------
139 
140 void SfxUndoAction::UndoWithContext( SfxUndoContext& i_context )
141 {
142     (void)i_context;
143     Undo();
144 }
145 
146 //------------------------------------------------------------------------
147 
148 void SfxUndoAction::Redo()
149 {
150 	// die sind nur konzeptuell pure virtual
151 	DBG_ERROR( "pure virtual function called: SfxUndoAction::Redo()" );
152 }
153 
154 //------------------------------------------------------------------------
155 
156 void SfxUndoAction::RedoWithContext( SfxUndoContext& i_context )
157 {
158     (void)i_context;
159     Redo();
160 }
161 
162 //------------------------------------------------------------------------
163 
164 void SfxUndoAction::Repeat(SfxRepeatTarget&)
165 {
166 	// die sind nur konzeptuell pure virtual
167 	DBG_ERROR( "pure virtual function called: SfxUndoAction::Repeat()" );
168 }
169 
170 //------------------------------------------------------------------------
171 
172 
173 sal_Bool SfxUndoAction::CanRepeat(SfxRepeatTarget&) const
174 {
175 	return sal_True;
176 }
177 
178 //========================================================================
179 
180 typedef ::std::vector< SfxUndoListener* >   UndoListeners;
181 
182 struct SVL_DLLPRIVATE SfxUndoManager_Data
183 {
184     ::osl::Mutex    aMutex;
185 	SfxUndoArray*   pUndoArray;
186 	SfxUndoArray*   pActUndoArray;
187 	SfxUndoArray*   pFatherUndoArray;
188 
189     sal_Int32       mnMarks;
190     sal_Int32       mnEmptyMark;
191     bool            mbUndoEnabled;
192     bool            mbDoing;
193     bool            mbClearUntilTopLevel;
194 
195     UndoListeners   aListeners;
196 
197     SfxUndoManager_Data( size_t i_nMaxUndoActionCount )
198         :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount ) )
199         ,pActUndoArray( NULL )
200         ,pFatherUndoArray( NULL )
201         ,mnMarks( 0 )
202         ,mnEmptyMark(MARK_INVALID)
203         ,mbUndoEnabled( true )
204         ,mbDoing( false )
205         ,mbClearUntilTopLevel( false )
206     {
207 	    pActUndoArray = pUndoArray;
208     }
209 
210     ~SfxUndoManager_Data()
211     {
212         delete pUndoArray;
213     }
214 };
215 
216 //========================================================================
217 
218 namespace svl { namespace undo { namespace impl
219 {
220     //--------------------------------------------------------------------
221     class SVL_DLLPRIVATE LockGuard
222     {
223     public:
224         LockGuard( SfxUndoManager& i_manager )
225             :m_manager( i_manager )
226         {
227             m_manager.ImplEnableUndo_Lock( false );
228         }
229 
230         ~LockGuard()
231         {
232             m_manager.ImplEnableUndo_Lock( true );
233         }
234 
235     private:
236         SfxUndoManager& m_manager;
237     };
238 
239     //--------------------------------------------------------------------
240     typedef void ( SfxUndoListener::*UndoListenerVoidMethod )();
241     typedef void ( SfxUndoListener::*UndoListenerStringMethod )( const String& );
242 
243     //--------------------------------------------------------------------
244     struct SVL_DLLPRIVATE NotifyUndoListener : public ::std::unary_function< SfxUndoListener*, void >
245     {
246         NotifyUndoListener()
247             :m_notificationMethod( NULL )
248             ,m_altNotificationMethod( NULL )
249             ,m_sActionComment()
250         {
251         }
252 
253         NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
254             :m_notificationMethod( i_notificationMethod )
255             ,m_altNotificationMethod( NULL )
256             ,m_sActionComment()
257         {
258         }
259 
260         NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const String& i_actionComment )
261             :m_notificationMethod( NULL )
262             ,m_altNotificationMethod( i_notificationMethod )
263             ,m_sActionComment( i_actionComment )
264         {
265         }
266 
267         bool is() const
268         {
269             return ( m_notificationMethod != NULL ) || ( m_altNotificationMethod != NULL );
270         }
271 
272         void operator()( SfxUndoListener* i_listener ) const
273         {
274             OSL_PRECOND( is(), "NotifyUndoListener: this will crash!" );
275             if ( m_altNotificationMethod != NULL )
276             {
277                 ( i_listener->*m_altNotificationMethod )( m_sActionComment );
278             }
279             else
280             {
281                 ( i_listener->*m_notificationMethod )();
282             }
283         }
284 
285     private:
286         UndoListenerVoidMethod      m_notificationMethod;
287         UndoListenerStringMethod    m_altNotificationMethod;
288         String                      m_sActionComment;
289     };
290 
291     //--------------------------------------------------------------------
292     class SVL_DLLPRIVATE UndoManagerGuard
293     {
294     public:
295         UndoManagerGuard( SfxUndoManager_Data& i_managerData )
296             :m_rManagerData( i_managerData )
297             ,m_aGuard( i_managerData.aMutex )
298             ,m_notifiers()
299         {
300         }
301 
302         ~UndoManagerGuard();
303 
304         void clear()
305         {
306             m_aGuard.clear();
307         }
308 
309         void reset()
310         {
311             m_aGuard.reset();
312         }
313 
314         void cancelNotifications()
315         {
316             m_notifiers.clear();
317         }
318 
319         /** marks the given Undo action for deletion
320 
321             The Undo action will be put into a list, whose members will be deleted from within the destructor of the
322             UndoManagerGuard. This deletion will happen without the UndoManager's mutex locked.
323         */
324         void    markForDeletion( SfxUndoAction* i_action )
325         {
326             // remember
327             if ( i_action )
328                 m_aUndoActionsCleanup.push_back( i_action );
329         }
330 
331         /** schedules the given SfxUndoListener method to be called for all registered listeners.
332 
333             The notification will happen after the Undo manager's mutex has been released, and after all pending
334             deletions of Undo actions are done.
335         */
336         void    scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
337         {
338             m_notifiers.push_back( NotifyUndoListener( i_notificationMethod ) );
339         }
340 
341         void    scheduleNotification( UndoListenerStringMethod i_notificationMethod, const String& i_actionComment )
342         {
343             m_notifiers.push_back( NotifyUndoListener( i_notificationMethod, i_actionComment ) );
344         }
345 
346     private:
347         SfxUndoManager_Data&                m_rManagerData;
348         ::osl::ResettableMutexGuard         m_aGuard;
349         ::std::list< SfxUndoAction* >       m_aUndoActionsCleanup;
350         ::std::list< NotifyUndoListener >   m_notifiers;
351     };
352 
353     UndoManagerGuard::~UndoManagerGuard()
354     {
355         // copy members
356         UndoListeners aListenersCopy( m_rManagerData.aListeners );
357 
358         // release mutex
359         m_aGuard.clear();
360 
361         // delete all actions
362         while ( !m_aUndoActionsCleanup.empty() )
363         {
364             SfxUndoAction* pAction = m_aUndoActionsCleanup.front();
365             m_aUndoActionsCleanup.pop_front();
366             try
367             {
368                 delete pAction;
369             }
370             catch( const Exception& )
371             {
372         	    DBG_UNHANDLED_EXCEPTION();
373             }
374         }
375 
376         // handle scheduled notification
377         for (   ::std::list< NotifyUndoListener >::const_iterator notifier = m_notifiers.begin();
378                 notifier != m_notifiers.end();
379                 ++notifier
380              )
381         {
382             if ( notifier->is() )
383                 ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), *notifier );
384         }
385     }
386 } } }
387 
388 using namespace ::svl::undo::impl;
389 
390 //========================================================================
391 
392 SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
393     :m_pData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
394 {
395 }
396 
397 //------------------------------------------------------------------------
398 
399 SfxUndoManager::~SfxUndoManager()
400 {
401     UndoListeners aListenersCopy;
402     {
403         UndoManagerGuard aGuard( *m_pData );
404         aListenersCopy = m_pData->aListeners;
405     }
406 
407     ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(),
408         NotifyUndoListener( &SfxUndoListener::undoManagerDying ) );
409 }
410 
411 //------------------------------------------------------------------------
412 
413 void SfxUndoManager::EnableUndo( bool i_enable )
414 {
415     UndoManagerGuard aGuard( *m_pData );
416     ImplEnableUndo_Lock( i_enable );
417 
418 }
419 
420 //------------------------------------------------------------------------
421 
422 void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
423 {
424     if ( m_pData->mbUndoEnabled == i_enable )
425         return;
426     m_pData->mbUndoEnabled = i_enable;
427 }
428 
429 //------------------------------------------------------------------------
430 
431 bool SfxUndoManager::IsUndoEnabled() const
432 {
433     UndoManagerGuard aGuard( *m_pData );
434     return ImplIsUndoEnabled_Lock();
435 }
436 
437 //------------------------------------------------------------------------
438 
439 bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
440 {
441 	return m_pData->mbUndoEnabled;
442 }
443 
444 //------------------------------------------------------------------------
445 
446 void SfxUndoManager::SetMaxUndoActionCount( size_t nMaxUndoActionCount )
447 {
448     UndoManagerGuard aGuard( *m_pData );
449 
450 	// Remove entries from the pActUndoArray when we have to reduce
451 	// the number of entries due to a lower nMaxUndoActionCount.
452 	// Both redo and undo action entries will be removed until we reached the
453 	// new nMaxUndoActionCount.
454 
455 	long nNumToDelete = m_pData->pActUndoArray->aUndoActions.size() - nMaxUndoActionCount;
456 	while ( nNumToDelete > 0 )
457 	{
458 		size_t nPos = m_pData->pActUndoArray->aUndoActions.size();
459 		if ( nPos > m_pData->pActUndoArray->nCurUndoAction )
460 		{
461             SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[nPos-1].pAction;
462 			if ( !pAction->IsLinked() )
463 			{
464                 aGuard.markForDeletion( pAction );
465                 m_pData->pActUndoArray->aUndoActions.Remove( nPos-1 );
466 				--nNumToDelete;
467 			}
468 		}
469 
470 		if ( nNumToDelete > 0 && m_pData->pActUndoArray->nCurUndoAction > 0 )
471 		{
472             SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
473 			if ( !pAction->IsLinked() )
474 			{
475                 aGuard.markForDeletion( pAction );
476                 m_pData->pActUndoArray->aUndoActions.Remove(0);
477 				--m_pData->pActUndoArray->nCurUndoAction;
478 				--nNumToDelete;
479 			}
480 		}
481 
482 		if ( nPos == m_pData->pActUndoArray->aUndoActions.size() )
483 			break; // Cannot delete more entries
484 	}
485 
486 	m_pData->pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
487 }
488 
489 //------------------------------------------------------------------------
490 
491 size_t SfxUndoManager::GetMaxUndoActionCount() const
492 {
493     UndoManagerGuard aGuard( *m_pData );
494 	return m_pData->pActUndoArray->nMaxUndoActions;
495 }
496 
497 //------------------------------------------------------------------------
498 
499 void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard& i_guard )
500 {
501     // clear array
502 	while ( !m_pData->pActUndoArray->aUndoActions.empty() )
503 	{
504         size_t deletePos = m_pData->pActUndoArray->aUndoActions.size() - 1;
505         SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ deletePos ].pAction;
506         i_guard.markForDeletion( pAction );
507         m_pData->pActUndoArray->aUndoActions.Remove( deletePos );
508 	}
509 
510 	m_pData->pActUndoArray->nCurUndoAction = 0;
511 
512     m_pData->mnMarks = 0;
513     m_pData->mnEmptyMark = MARK_INVALID;
514 }
515 
516 //------------------------------------------------------------------------
517 
518 void SfxUndoManager::Clear()
519 {
520     UndoManagerGuard aGuard( *m_pData );
521 
522     OSL_ENSURE( !ImplIsInListAction_Lock(), "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
523     ImplClearCurrentLevel_NoNotify( aGuard );
524 
525     // notify listeners
526     aGuard.scheduleNotification( &SfxUndoListener::cleared );
527 }
528 
529 //------------------------------------------------------------------------
530 
531 void SfxUndoManager::ClearAllLevels()
532 {
533     UndoManagerGuard aGuard( *m_pData );
534     ImplClearCurrentLevel_NoNotify( aGuard );
535 
536     if ( ImplIsInListAction_Lock() )
537     {
538         m_pData->mbClearUntilTopLevel = true;
539     }
540     else
541     {
542         aGuard.scheduleNotification( &SfxUndoListener::cleared );
543     }
544 }
545 
546 //------------------------------------------------------------------------
547 
548 void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
549 {
550     UndoManagerGuard aGuard( *m_pData );
551     ImplClearRedo( aGuard, i_currentLevel );
552 }
553 
554 //------------------------------------------------------------------------
555 
556 void SfxUndoManager::ClearRedo()
557 {
558     OSL_ENSURE( !IsInListAction(), "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
559     ImplClearRedo_NoLock( CurrentLevel );
560 }
561 
562 //------------------------------------------------------------------------
563 
564 void SfxUndoManager::Reset()
565 {
566     UndoManagerGuard aGuard( *m_pData );
567 
568     // clear all locks
569     while ( !ImplIsUndoEnabled_Lock() )
570         ImplEnableUndo_Lock( true );
571 
572     // cancel all list actions
573     while ( IsInListAction() )
574         ImplLeaveListAction( false, aGuard );
575 
576     // clear both stacks
577     ImplClearCurrentLevel_NoNotify( aGuard );
578 
579     // cancel the notifications scheduled by ImplLeaveListAction,
580     // as we want to do an own, dedicated notification
581     aGuard.cancelNotifications();
582 
583     // schedule notification
584     aGuard.scheduleNotification( &SfxUndoListener::resetAll );
585 }
586 
587 //------------------------------------------------------------------------
588 
589 void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
590 {
591     while ( m_pData->pActUndoArray->nCurUndoAction > 0 )
592     {
593         SfxUndoAction* pUndoAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
594         m_pData->pActUndoArray->aUndoActions.Remove( 0 );
595         i_guard.markForDeletion( pUndoAction );
596         --m_pData->pActUndoArray->nCurUndoAction;
597     }
598     // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
599 }
600 
601 //------------------------------------------------------------------------
602 
603 void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard, bool const i_currentLevel )
604 {
605     SfxUndoArray* pUndoArray = ( i_currentLevel == IUndoManager::CurrentLevel ) ? m_pData->pActUndoArray : m_pData->pUndoArray;
606 
607     // clearance
608 	while ( pUndoArray->aUndoActions.size() > pUndoArray->nCurUndoAction )
609 	{
610         size_t deletePos = pUndoArray->aUndoActions.size() - 1;
611 		SfxUndoAction* pAction = pUndoArray->aUndoActions[ deletePos ].pAction;
612 		pUndoArray->aUndoActions.Remove( deletePos );
613         i_guard.markForDeletion( pAction );
614 	}
615 
616     // notification - only if the top level's stack was cleared
617     if ( i_currentLevel == IUndoManager::TopLevel )
618         i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
619 }
620 
621 //------------------------------------------------------------------------
622 
623 bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction *pAction, bool bTryMerge, bool bClearRedo, UndoManagerGuard& i_guard )
624 {
625 	if ( !ImplIsUndoEnabled_Lock() || ( m_pData->pActUndoArray->nMaxUndoActions == 0 ) )
626     {
627         i_guard.markForDeletion( pAction );
628         return false;
629     }
630 
631     // merge, if required
632 	SfxUndoAction* pMergeWithAction = m_pData->pActUndoArray->nCurUndoAction ?
633 		m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction : NULL;
634 	if ( bTryMerge && ( !pMergeWithAction || !pMergeWithAction->Merge( pAction ) ) )
635     {
636         i_guard.markForDeletion( pAction );
637         return false;
638     }
639 
640 	// clear redo stack, if requested
641     if ( bClearRedo && ( ImplGetRedoActionCount_Lock( CurrentLevel ) > 0 ) )
642         ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
643 
644 	// respect max number
645 	if( m_pData->pActUndoArray == m_pData->pUndoArray )
646     {
647 		while( m_pData->pActUndoArray->aUndoActions.size() >=
648 			   m_pData->pActUndoArray->nMaxUndoActions &&
649 			   !m_pData->pActUndoArray->aUndoActions[0].pAction->IsLinked() )
650 		{
651             i_guard.markForDeletion( m_pData->pActUndoArray->aUndoActions[0].pAction );
652 			m_pData->pActUndoArray->aUndoActions.Remove(0);
653 			--m_pData->pActUndoArray->nCurUndoAction;
654 		}
655     }
656 
657 	// append new action
658 	m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ );
659     return true;
660 }
661 
662 //------------------------------------------------------------------------
663 
664 void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerge )
665 {
666     UndoManagerGuard aGuard( *m_pData );
667 
668     // add
669     if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
670     {
671         // notify listeners
672         aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
673     }
674 }
675 
676 //------------------------------------------------------------------------
677 
678 size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
679 {
680     UndoManagerGuard aGuard( *m_pData );
681     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
682 	return pUndoArray->nCurUndoAction;
683 }
684 
685 //------------------------------------------------------------------------
686 
687 XubString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
688 {
689     UndoManagerGuard aGuard( *m_pData );
690 
691     String sComment;
692     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
693     DBG_ASSERT( nNo < pUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" );
694 	if( nNo < pUndoArray->nCurUndoAction )
695 	{
696 		sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
697 	}
698     return sComment;
699 }
700 
701 //------------------------------------------------------------------------
702 
703 sal_uInt16 SfxUndoManager::GetUndoActionId() const
704 {
705     UndoManagerGuard aGuard( *m_pData );
706 
707     DBG_ASSERT( m_pData->pActUndoArray->nCurUndoAction > 0, "svl::SfxUndoManager::GetUndoActionId(), illegal id!" );
708 	if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
709         return 0;
710 	return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction->GetId();
711 }
712 
713 //------------------------------------------------------------------------
714 
715 SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const
716 {
717     UndoManagerGuard aGuard( *m_pData );
718 
719 	DBG_ASSERT( nNo < m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
720 	if( nNo >= m_pData->pActUndoArray->nCurUndoAction )
721         return NULL;
722 	return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1-nNo].pAction;
723 }
724 
725 //------------------------------------------------------------------------
726 
727 /** clears the redo stack and removes the top undo action */
728 void SfxUndoManager::RemoveLastUndoAction()
729 {
730     UndoManagerGuard aGuard( *m_pData );
731 
732 	ENSURE_OR_RETURN_VOID( m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
733 
734     m_pData->pActUndoArray->nCurUndoAction--;
735 
736 	// delete redo-actions and top action
737 	for ( size_t nPos = m_pData->pActUndoArray->aUndoActions.size(); nPos > m_pData->pActUndoArray->nCurUndoAction; --nPos )
738     {
739         aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[nPos-1].pAction );
740     }
741 
742 	m_pData->pActUndoArray->aUndoActions.Remove(
743 		m_pData->pActUndoArray->nCurUndoAction,
744 		m_pData->pActUndoArray->aUndoActions.size() - m_pData->pActUndoArray->nCurUndoAction );
745 }
746 
747 //------------------------------------------------------------------------
748 
749 bool SfxUndoManager::IsDoing() const
750 {
751     UndoManagerGuard aGuard( *m_pData );
752     return m_pData->mbDoing;
753 }
754 
755 //------------------------------------------------------------------------
756 
757 sal_Bool SfxUndoManager::Undo()
758 {
759     return ImplUndo( NULL );
760 }
761 
762 //------------------------------------------------------------------------
763 
764 sal_Bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
765 {
766     return ImplUndo( &i_context );
767 }
768 
769 //------------------------------------------------------------------------
770 
771 sal_Bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
772 {
773     UndoManagerGuard aGuard( *m_pData );
774     OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
775 
776     ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
777     LockGuard aLockGuard( *this );
778 
779     if ( ImplIsInListAction_Lock() )
780     {
781         OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" );
782         return sal_False;
783     }
784 
785     if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
786     {
787         OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" );
788         return sal_False;
789     }
790 
791     SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ].pAction;
792     const String sActionComment = pAction->GetComment();
793     try
794     {
795         // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
796         // nowadays ...
797         aGuard.clear();
798         if ( i_contextOrNull != NULL )
799             pAction->UndoWithContext( *i_contextOrNull );
800         else
801             pAction->Undo();
802         aGuard.reset();
803     }
804     catch( ... )
805     {
806         aGuard.reset();
807 
808         // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
809         // we still find pAction in our current Undo array
810         size_t nCurAction = 0;
811         while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
812         {
813             if ( m_pData->pActUndoArray->aUndoActions[ nCurAction++ ].pAction == pAction )
814             {
815                 // the Undo action is still there ...
816                 // assume the error is a permanent failure, and clear the Undo stack
817                 ImplClearUndo( aGuard );
818                 throw;
819             }
820         }
821         OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
822         throw;
823     }
824 
825     aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
826 
827     return sal_True;
828 }
829 
830 //------------------------------------------------------------------------
831 
832 size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
833 {
834     UndoManagerGuard aGuard( *m_pData );
835     return ImplGetRedoActionCount_Lock( i_currentLevel );
836 }
837 
838 //------------------------------------------------------------------------
839 
840 size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
841 {
842     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
843 	return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction;
844 }
845 
846 //------------------------------------------------------------------------
847 
848 XubString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
849 {
850     UndoManagerGuard aGuard( *m_pData );
851     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
852 	return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
853 }
854 
855 //------------------------------------------------------------------------
856 
857 sal_Bool SfxUndoManager::Redo()
858 {
859     return ImplRedo( NULL );
860 }
861 
862 //------------------------------------------------------------------------
863 
864 sal_Bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
865 {
866     return ImplRedo( &i_context );
867 }
868 
869 //------------------------------------------------------------------------
870 
871 sal_Bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
872 {
873     UndoManagerGuard aGuard( *m_pData );
874     OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
875 
876     ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
877     LockGuard aLockGuard( *this );
878 
879     if ( ImplIsInListAction_Lock() )
880     {
881         OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
882         return sal_False;
883     }
884 
885     if ( m_pData->pActUndoArray->nCurUndoAction >= m_pData->pActUndoArray->aUndoActions.size() )
886     {
887         OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" );
888         return sal_False;
889     }
890 
891     SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction;
892     const String sActionComment = pAction->GetComment();
893     try
894     {
895         // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
896         // nowadays ...
897         aGuard.clear();
898         if ( i_contextOrNull != NULL )
899             pAction->RedoWithContext( *i_contextOrNull );
900         else
901             pAction->Redo();
902         aGuard.reset();
903     }
904     catch( ... )
905     {
906         aGuard.reset();
907 
908         // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
909         // we still find pAction in our current Undo array
910         size_t nCurAction = 0;
911         while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
912         {
913             if ( m_pData->pActUndoArray->aUndoActions[ nCurAction ].pAction == pAction )
914             {
915                 // the Undo action is still there ...
916                 // assume the error is a permanent failure, and clear the Undo stack
917                 ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
918                 throw;
919             }
920             ++nCurAction;
921         }
922         OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
923         throw;
924     }
925 
926     aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
927 
928 	return sal_True;
929 }
930 
931 //------------------------------------------------------------------------
932 
933 size_t SfxUndoManager::GetRepeatActionCount() const
934 {
935     UndoManagerGuard aGuard( *m_pData );
936 	return m_pData->pActUndoArray->aUndoActions.size();
937 }
938 
939 //------------------------------------------------------------------------
940 
941 XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) const
942 {
943     UndoManagerGuard aGuard( *m_pData );
944 	return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction
945 		->GetRepeatComment(rTarget);
946 }
947 
948 //------------------------------------------------------------------------
949 
950 sal_Bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
951 {
952     UndoManagerGuard aGuard( *m_pData );
953 	if ( !m_pData->pActUndoArray->aUndoActions.empty() )
954 	{
955         SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction;
956         aGuard.clear();
957         if ( pAction->CanRepeat( rTarget ) )
958             pAction->Repeat( rTarget );
959 		return sal_True;
960 	}
961 
962 	return sal_False;
963 }
964 
965 //------------------------------------------------------------------------
966 
967 sal_Bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
968 {
969     UndoManagerGuard aGuard( *m_pData );
970 	if ( !m_pData->pActUndoArray->aUndoActions.empty() )
971 	{
972 		size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1;
973 		return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
974 	}
975 	return sal_False;
976 }
977 
978 //------------------------------------------------------------------------
979 
980 void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
981 {
982     UndoManagerGuard aGuard( *m_pData );
983     m_pData->aListeners.push_back( &i_listener );
984 }
985 
986 //------------------------------------------------------------------------
987 
988 void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
989 {
990     UndoManagerGuard aGuard( *m_pData );
991     for (   UndoListeners::iterator lookup = m_pData->aListeners.begin();
992             lookup != m_pData->aListeners.end();
993             ++lookup
994         )
995     {
996         if ( (*lookup) == &i_listener )
997         {
998             m_pData->aListeners.erase( lookup );
999             break;
1000         }
1001     }
1002 }
1003 
1004 //------------------------------------------------------------------------
1005 
1006 void SfxUndoManager::EnterListAction(
1007 	const XubString& rComment, const XubString &rRepeatComment, sal_uInt16 nId )
1008 
1009 /*	[Beschreibung]
1010 
1011 	Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles.
1012 */
1013 
1014 {
1015     UndoManagerGuard aGuard( *m_pData );
1016 
1017     if( !ImplIsUndoEnabled_Lock() )
1018 		return;
1019 
1020 	if ( !m_pData->pUndoArray->nMaxUndoActions )
1021 		return;
1022 
1023 	m_pData->pFatherUndoArray = m_pData->pActUndoArray;
1024 	SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
1025     OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) );
1026         // expected to succeed: all conditions under which it could fail should have been checked already
1027 	m_pData->pActUndoArray = pAction;
1028 
1029     // notification
1030     aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
1031 }
1032 
1033 //------------------------------------------------------------------------
1034 
1035 bool SfxUndoManager::IsInListAction() const
1036 {
1037     UndoManagerGuard aGuard( *m_pData );
1038     return ImplIsInListAction_Lock();
1039 }
1040 
1041 //------------------------------------------------------------------------
1042 
1043 bool SfxUndoManager::ImplIsInListAction_Lock() const
1044 {
1045     return ( m_pData->pActUndoArray != m_pData->pUndoArray );
1046 }
1047 
1048 //------------------------------------------------------------------------
1049 
1050 size_t SfxUndoManager::GetListActionDepth() const
1051 {
1052     UndoManagerGuard aGuard( *m_pData );
1053     size_t nDepth(0);
1054 
1055     SfxUndoArray* pLookup( m_pData->pActUndoArray );
1056     while ( pLookup != m_pData->pUndoArray )
1057     {
1058         pLookup = pLookup->pFatherUndoArray;
1059         ++nDepth;
1060     }
1061 
1062     return nDepth;
1063 }
1064 
1065 //------------------------------------------------------------------------
1066 
1067 size_t SfxUndoManager::LeaveListAction()
1068 {
1069     UndoManagerGuard aGuard( *m_pData );
1070     size_t nCount = ImplLeaveListAction( false, aGuard );
1071 
1072     if ( m_pData->mbClearUntilTopLevel )
1073     {
1074         ImplClearCurrentLevel_NoNotify( aGuard );
1075         if ( !ImplIsInListAction_Lock() )
1076         {
1077             m_pData->mbClearUntilTopLevel = false;
1078             aGuard.scheduleNotification( &SfxUndoListener::cleared );
1079         }
1080         nCount = 0;
1081     }
1082 
1083     return nCount;
1084 }
1085 
1086 //------------------------------------------------------------------------
1087 
1088 size_t SfxUndoManager::LeaveAndMergeListAction()
1089 {
1090     UndoManagerGuard aGuard( *m_pData );
1091     return ImplLeaveListAction( true, aGuard );
1092 }
1093 
1094 //------------------------------------------------------------------------
1095 
1096 size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
1097 {
1098     if ( !ImplIsUndoEnabled_Lock() )
1099 		return 0;
1100 
1101 	if ( !m_pData->pUndoArray->nMaxUndoActions )
1102 		return 0;
1103 
1104 	if( !ImplIsInListAction_Lock() )
1105 	{
1106 		DBG_ERROR( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
1107 		return 0;
1108 	}
1109 
1110 	DBG_ASSERT( m_pData->pActUndoArray->pFatherUndoArray, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" );
1111 
1112     // the array/level which we're about to leave
1113 	SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray;
1114     // one step up
1115 	m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray;
1116 
1117 	// If no undo actions were added to the list, delete the list action
1118     const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
1119 	if ( nListActionElements == 0 )
1120 	{
1121 	    SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1122 		m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
1123         i_guard.markForDeletion( pCurrentAction );
1124 
1125         i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
1126         return 0;
1127     }
1128 
1129     // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1130     // the redo stack
1131     ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
1132 
1133     SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1134 	SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
1135     ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
1136 
1137     if ( i_merge )
1138     {
1139         // merge the list action with its predecessor on the same level
1140         OSL_ENSURE( m_pData->pActUndoArray->nCurUndoAction > 1,
1141             "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
1142         if ( m_pData->pActUndoArray->nCurUndoAction > 1 )
1143         {
1144             SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction;
1145             m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 );
1146             --m_pData->pActUndoArray->nCurUndoAction;
1147             pListAction->aUndoActions.Insert( pPreviousAction, 0 );
1148             ++pListAction->nCurUndoAction;
1149 
1150             pListAction->SetComment( pPreviousAction->GetComment() );
1151         }
1152     }
1153 
1154     // if the undo array has no comment, try to get it from its children
1155 	if ( pListAction->GetComment().Len() == 0 )
1156 	{
1157 		for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
1158 		{
1159 			if( pListAction->aUndoActions[n].pAction->GetComment().Len() )
1160 			{
1161 				pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
1162 				break;
1163 			}
1164 		}
1165 	}
1166 
1167     // notify listeners
1168     i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
1169 
1170     // outta here
1171     return nListActionElements;
1172 }
1173 
1174 //------------------------------------------------------------------------
1175 UndoStackMark SfxUndoManager::MarkTopUndoAction()
1176 {
1177     UndoManagerGuard aGuard( *m_pData );
1178 
1179     OSL_ENSURE( !IsInListAction(),
1180             "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
1181     OSL_ENSURE((m_pData->mnMarks + 1) < (m_pData->mnEmptyMark - 1),
1182             "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
1183 
1184     size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction;
1185     if (0 == nActionPos)
1186     {
1187         --m_pData->mnEmptyMark;
1188         return m_pData->mnEmptyMark;
1189     }
1190 
1191     m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
1192             ++m_pData->mnMarks );
1193     return m_pData->mnMarks;
1194 }
1195 
1196 //------------------------------------------------------------------------
1197 void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
1198 {
1199     UndoManagerGuard aGuard( *m_pData );
1200 
1201     if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
1202     {
1203         return; // nothing to remove
1204     }
1205     else if (i_mark == m_pData->mnEmptyMark)
1206     {
1207         --m_pData->mnEmptyMark; // never returned from MarkTop => invalid
1208         return;
1209     }
1210 
1211     for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i )
1212     {
1213         MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i];
1214         for (   ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
1215                 markPos != rAction.aMarks.end();
1216                 ++markPos
1217             )
1218         {
1219             if ( *markPos == i_mark )
1220             {
1221                 rAction.aMarks.erase( markPos );
1222                 return;
1223             }
1224         }
1225     }
1226     OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" );
1227         // TODO: this might be too offensive. There are situations where we implicitly remove marks
1228         // without our clients, in particular the client which created the mark, having a chance to know
1229         // about this.
1230 }
1231 
1232 //------------------------------------------------------------------------
1233 bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
1234 {
1235     UndoManagerGuard aGuard( *m_pData );
1236 
1237     size_t nActionPos = m_pData->pUndoArray->nCurUndoAction;
1238     if ( nActionPos == 0 )
1239     {
1240         return (i_mark == m_pData->mnEmptyMark);
1241     }
1242 
1243     const MarkedUndoAction& rAction =
1244             m_pData->pUndoArray->aUndoActions[ nActionPos-1 ];
1245     for (   ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
1246             markPos != rAction.aMarks.end();
1247             ++markPos
1248         )
1249     {
1250         if ( *markPos == i_mark )
1251             return true;
1252     }
1253 
1254     return false;
1255 }
1256 
1257 //------------------------------------------------------------------------
1258 
1259 void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count )
1260 {
1261     UndoManagerGuard aGuard( *m_pData );
1262 
1263     size_t nActionsToRemove = i_count;
1264     while ( nActionsToRemove )
1265     {
1266         SfxUndoAction* pActionToRemove = m_pData->pUndoArray->aUndoActions[0].pAction;
1267 
1268         if ( IsInListAction() && ( m_pData->pUndoArray->nCurUndoAction == 1 ) )
1269         {
1270             OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" );
1271             return;
1272         }
1273 
1274         aGuard.markForDeletion( pActionToRemove );
1275         m_pData->pUndoArray->aUndoActions.Remove( 0 );
1276         --m_pData->pUndoArray->nCurUndoAction;
1277         --nActionsToRemove;
1278     }
1279 }
1280 
1281 //------------------------------------------------------------------------
1282 
1283 sal_uInt16 SfxListUndoAction::GetId() const
1284 {
1285 	return nId;
1286 }
1287 
1288 //------------------------------------------------------------------------
1289 
1290 XubString SfxListUndoAction::GetComment() const
1291 {
1292 	return aComment;
1293 }
1294 
1295 //------------------------------------------------------------------------
1296 
1297 void SfxListUndoAction::SetComment( const UniString& rComment )
1298 {
1299 	aComment = rComment;
1300 }
1301 
1302 //------------------------------------------------------------------------
1303 
1304 XubString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
1305 {
1306 	return aRepeatComment;
1307 }
1308 
1309 
1310 //------------------------------------------------------------------------
1311 
1312 SfxListUndoAction::SfxListUndoAction
1313 (
1314 	const XubString &rComment,
1315 	const XubString rRepeatComment,
1316 	sal_uInt16 Id,
1317 	SfxUndoArray *pFather
1318 )
1319 : nId(Id), aComment(rComment), aRepeatComment(rRepeatComment)
1320 {
1321 	pFatherUndoArray = pFather;
1322 	nMaxUndoActions = USHRT_MAX;
1323 }
1324 
1325 //------------------------------------------------------------------------
1326 
1327 void SfxListUndoAction::Undo()
1328 {
1329 	for(size_t i=nCurUndoAction;i>0;)
1330 		aUndoActions[--i].pAction->Undo();
1331 	nCurUndoAction=0;
1332 }
1333 
1334 //------------------------------------------------------------------------
1335 
1336 void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1337 {
1338 	for(size_t i=nCurUndoAction;i>0;)
1339 		aUndoActions[--i].pAction->UndoWithContext( i_context );
1340 	nCurUndoAction=0;
1341 }
1342 
1343 //------------------------------------------------------------------------
1344 
1345 void SfxListUndoAction::Redo()
1346 {
1347 	for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1348 		aUndoActions[i].pAction->Redo();
1349 	nCurUndoAction = aUndoActions.size();
1350 }
1351 
1352 //------------------------------------------------------------------------
1353 
1354 void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
1355 {
1356 	for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1357 		aUndoActions[i].pAction->RedoWithContext( i_context );
1358 	nCurUndoAction = aUndoActions.size();
1359 }
1360 
1361 //------------------------------------------------------------------------
1362 
1363 void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
1364 {
1365 	for(size_t i=0;i<nCurUndoAction;i++)
1366 		aUndoActions[i].pAction->Repeat(rTarget);
1367 }
1368 
1369 //------------------------------------------------------------------------
1370 
1371 sal_Bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r)  const
1372 {
1373 	for(size_t i=0;i<nCurUndoAction;i++)
1374 		if(!aUndoActions[i].pAction->CanRepeat(r))
1375 			return sal_False;
1376 	return sal_True;
1377 }
1378 
1379 //------------------------------------------------------------------------
1380 
1381 sal_Bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
1382 {
1383 	return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction );
1384 }
1385 
1386 //------------------------------------------------------------------------
1387 
1388 SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager)
1389 /*	[Beschreibung]
1390 
1391 	Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt.
1392 	Holt sich als zugehoerige Action des weiteren UndoManagers dessen
1393 	aktuelle Action.
1394 */
1395 
1396 {
1397 	pUndoManager = pManager;
1398     SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager );
1399     ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" );
1400         // yes, this cast is dirty. But reaching into the the SfxUndoManager's implementation,
1401         // directly accessing its internal stack, and tampering with an action on that stack
1402         // is dirty, too.
1403 	if ( pManager->GetMaxUndoActionCount() )
1404 	{
1405 		size_t nPos = pManager->GetUndoActionCount()-1;
1406 		pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction;
1407 		pAction->SetLinked();
1408 	}
1409 	else
1410 		pAction = 0;
1411 }
1412 
1413 //------------------------------------------------------------------------
1414 
1415 void SfxLinkUndoAction::Undo()
1416 {
1417 	if ( pAction )
1418 		pUndoManager->Undo();
1419 }
1420 
1421 //------------------------------------------------------------------------
1422 
1423 void SfxLinkUndoAction::Redo()
1424 {
1425 	if ( pAction )
1426 		pUndoManager->Redo();
1427 }
1428 
1429 //------------------------------------------------------------------------
1430 
1431 
1432 sal_Bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
1433 {
1434 	return pAction && pAction->CanRepeat(r);
1435 }
1436 
1437 
1438 //------------------------------------------------------------------------
1439 
1440 
1441 void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r)
1442 {
1443 	if ( pAction && pAction->CanRepeat( r ) )
1444 	    pAction->Repeat( r );
1445 }
1446 
1447 
1448 //------------------------------------------------------------------------
1449 
1450 XubString SfxLinkUndoAction::GetComment() const
1451 {
1452 	if ( pAction )
1453 		return pAction->GetComment();
1454 	else
1455 		return XubString();
1456 }
1457 
1458 
1459 //------------------------------------------------------------------------
1460 
1461 XubString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
1462 {
1463 	if ( pAction )
1464 		return pAction->GetRepeatComment(r);
1465 	else
1466 		return XubString();
1467 }
1468 
1469 //------------------------------------------------------------------------
1470 
1471 SfxLinkUndoAction::~SfxLinkUndoAction()
1472 {
1473 	if( pAction )
1474 		pAction->SetLinked( sal_False );
1475 }
1476 
1477 
1478 //------------------------------------------------------------------------
1479 
1480 SfxUndoArray::~SfxUndoArray()
1481 {
1482 	while ( !aUndoActions.empty() )
1483 	{
1484 		SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
1485 		aUndoActions.Remove( aUndoActions.size() - 1 );
1486 		delete pAction;
1487 	}
1488 }
1489 
1490 
1491 sal_uInt16 SfxLinkUndoAction::GetId() const
1492 {
1493       return pAction ? pAction->GetId() : 0;
1494 }
1495