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