xref: /trunk/main/svl/source/undo/undo.cxx (revision 6b1f65abf26fe78aaf4df775c7ae34eca0297fb1)
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