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