xref: /aoo4110/main/svl/source/undo/undo.cxx (revision b1cdbd2c)
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 
~SfxRepeatTarget()54 SfxRepeatTarget::~SfxRepeatTarget()
55 {
56 }
57 
58 //------------------------------------------------------------------------
59 
~SfxUndoContext()60 SfxUndoContext::~SfxUndoContext()
61 {
62 }
63 
64 //------------------------------------------------------------------------
65 
SetLinkToSfxLinkUndoAction(SfxLinkUndoAction * pSfxLinkUndoAction)66 void SfxUndoAction::SetLinkToSfxLinkUndoAction(SfxLinkUndoAction* pSfxLinkUndoAction)
67 {
68     mpSfxLinkUndoAction = pSfxLinkUndoAction;
69 }
70 
71 //------------------------------------------------------------------------
72 
~SfxUndoAction()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 
SfxUndoAction()85 SfxUndoAction::SfxUndoAction()
86 :   mpSfxLinkUndoAction(0)
87 {
88 	DBG_CTOR(SfxUndoAction, 0);
89 }
90 
91 //------------------------------------------------------------------------
92 
Merge(SfxUndoAction *)93 sal_Bool SfxUndoAction::Merge( SfxUndoAction * )
94 {
95 	DBG_CHKTHIS(SfxUndoAction, 0);
96 	return sal_False;
97 }
98 
99 //------------------------------------------------------------------------
100 
GetComment() const101 XubString SfxUndoAction::GetComment() const
102 {
103 	DBG_CHKTHIS(SfxUndoAction, 0);
104 	return XubString();
105 }
106 
107 //------------------------------------------------------------------------
108 
109 
GetId() const110 sal_uInt16 SfxUndoAction::GetId() const
111 {
112 	DBG_CHKTHIS(SfxUndoAction, 0);
113 	return 0;
114 }
115 
116 //------------------------------------------------------------------------
117 
GetRepeatComment(SfxRepeatTarget &) const118 XubString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
119 {
120 	DBG_CHKTHIS(SfxUndoAction, 0);
121 	return GetComment();
122 }
123 
124 //------------------------------------------------------------------------
125 
Undo()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 
UndoWithContext(SfxUndoContext & i_context)134 void SfxUndoAction::UndoWithContext( SfxUndoContext& i_context )
135 {
136     (void)i_context;
137     Undo();
138 }
139 
140 //------------------------------------------------------------------------
141 
Redo()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 
RedoWithContext(SfxUndoContext & i_context)150 void SfxUndoAction::RedoWithContext( SfxUndoContext& i_context )
151 {
152     (void)i_context;
153     Redo();
154 }
155 
156 //------------------------------------------------------------------------
157 
Repeat(SfxRepeatTarget &)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 
CanRepeat(SfxRepeatTarget &) const167 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 
SfxUndoManager_DataSfxUndoManager_Data191     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 
~SfxUndoManager_DataSfxUndoManager_Data204     ~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:
LockGuard(SfxUndoManager & i_manager)218         LockGuard( SfxUndoManager& i_manager )
219             :m_manager( i_manager )
220         {
221             m_manager.ImplEnableUndo_Lock( false );
222         }
223 
~LockGuard()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     {
NotifyUndoListenersvl::undo::impl::NotifyUndoListener240         NotifyUndoListener()
241             :m_notificationMethod( NULL )
242             ,m_altNotificationMethod( NULL )
243             ,m_sActionComment()
244         {
245         }
246 
NotifyUndoListenersvl::undo::impl::NotifyUndoListener247         NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
248             :m_notificationMethod( i_notificationMethod )
249             ,m_altNotificationMethod( NULL )
250             ,m_sActionComment()
251         {
252         }
253 
NotifyUndoListenersvl::undo::impl::NotifyUndoListener254         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 
issvl::undo::impl::NotifyUndoListener261         bool is() const
262         {
263             return ( m_notificationMethod != NULL ) || ( m_altNotificationMethod != NULL );
264         }
265 
operator ()svl::undo::impl::NotifyUndoListener266         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:
UndoManagerGuard(SfxUndoManager_Data & i_managerData)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 
clear()298         void clear()
299         {
300             m_aGuard.clear();
301         }
302 
reset()303         void reset()
304         {
305             m_aGuard.reset();
306         }
307 
cancelNotifications()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         */
markForDeletion(SfxUndoAction * i_action)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         */
scheduleNotification(UndoListenerVoidMethod i_notificationMethod)330         void    scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
331         {
332             m_notifiers.push_back( NotifyUndoListener( i_notificationMethod ) );
333         }
334 
scheduleNotification(UndoListenerStringMethod i_notificationMethod,const String & i_actionComment)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 
~UndoManagerGuard()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 
SfxUndoManager(size_t nMaxUndoActionCount)386 SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
387     :m_pData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
388 {
389 }
390 
391 //------------------------------------------------------------------------
392 
~SfxUndoManager()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 
EnableUndo(bool i_enable)407 void SfxUndoManager::EnableUndo( bool i_enable )
408 {
409     UndoManagerGuard aGuard( *m_pData );
410     ImplEnableUndo_Lock( i_enable );
411 
412 }
413 
414 //------------------------------------------------------------------------
415 
ImplEnableUndo_Lock(bool const i_enable)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 
IsUndoEnabled() const425 bool SfxUndoManager::IsUndoEnabled() const
426 {
427     UndoManagerGuard aGuard( *m_pData );
428     return ImplIsUndoEnabled_Lock();
429 }
430 
431 //------------------------------------------------------------------------
432 
ImplIsUndoEnabled_Lock() const433 bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
434 {
435 	return m_pData->mbUndoEnabled;
436 }
437 
438 //------------------------------------------------------------------------
439 
SetMaxUndoActionCount(size_t nMaxUndoActionCount)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 
GetMaxUndoActionCount() const479 size_t SfxUndoManager::GetMaxUndoActionCount() const
480 {
481     UndoManagerGuard aGuard( *m_pData );
482 	return m_pData->pActUndoArray->nMaxUndoActions;
483 }
484 
485 //------------------------------------------------------------------------
486 
ImplClearCurrentLevel_NoNotify(UndoManagerGuard & i_guard)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 
Clear()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 
ClearAllLevels()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 
ImplClearRedo_NoLock(bool const i_currentLevel)536 void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
537 {
538     UndoManagerGuard aGuard( *m_pData );
539     ImplClearRedo( aGuard, i_currentLevel );
540 }
541 
542 //------------------------------------------------------------------------
543 
ClearRedo()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 
Reset()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 
ImplClearUndo(UndoManagerGuard & i_guard)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 
ImplClearRedo(UndoManagerGuard & i_guard,bool const i_currentLevel)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 
ImplAddUndoAction_NoNotify(SfxUndoAction * pAction,bool bTryMerge,bool bClearRedo,UndoManagerGuard & i_guard)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 
AddUndoAction(SfxUndoAction * pAction,sal_Bool bTryMerge)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 
GetUndoActionCount(bool const i_currentLevel) const664 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 
GetUndoActionComment(size_t nNo,bool const i_currentLevel) const673 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 
GetUndoActionId() const689 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 
GetUndoAction(size_t nNo) const701 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 */
RemoveLastUndoAction()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 
IsDoing() const735 bool SfxUndoManager::IsDoing() const
736 {
737     UndoManagerGuard aGuard( *m_pData );
738     return m_pData->mbDoing;
739 }
740 
741 //------------------------------------------------------------------------
742 
Undo()743 sal_Bool SfxUndoManager::Undo()
744 {
745     return ImplUndo( NULL );
746 }
747 
748 //------------------------------------------------------------------------
749 
UndoWithContext(SfxUndoContext & i_context)750 sal_Bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
751 {
752     return ImplUndo( &i_context );
753 }
754 
755 //------------------------------------------------------------------------
756 
ImplUndo(SfxUndoContext * i_contextOrNull)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 
GetRedoActionCount(bool const i_currentLevel) const818 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 
ImplGetRedoActionCount_Lock(bool const i_currentLevel) const826 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 
GetRedoAction(size_t nNo,bool const i_currentLevel) const834 SfxUndoAction* SfxUndoManager::GetRedoAction( size_t nNo, bool const i_currentLevel ) const
835 {
836     UndoManagerGuard aGuard( *m_pData );
837 
838     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
839     if ( (pUndoArray->nCurUndoAction + nNo) > pUndoArray->aUndoActions.size() )
840     {
841         return NULL;
842     }
843     return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction;
844 }
845 
846 //------------------------------------------------------------------------
847 
GetRedoActionComment(size_t nNo,bool const i_currentLevel) const848 XubString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
849 {
850     String sComment;
851     UndoManagerGuard aGuard( *m_pData );
852     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
853     if ( (pUndoArray->nCurUndoAction + nNo) < pUndoArray->aUndoActions.size() )
854     {
855         sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
856     }
857     return sComment;
858 }
859 
860 //------------------------------------------------------------------------
861 
Redo()862 sal_Bool SfxUndoManager::Redo()
863 {
864     return ImplRedo( NULL );
865 }
866 
867 //------------------------------------------------------------------------
868 
RedoWithContext(SfxUndoContext & i_context)869 sal_Bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
870 {
871     return ImplRedo( &i_context );
872 }
873 
874 //------------------------------------------------------------------------
875 
ImplRedo(SfxUndoContext * i_contextOrNull)876 sal_Bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
877 {
878     UndoManagerGuard aGuard( *m_pData );
879     OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
880 
881     ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
882     LockGuard aLockGuard( *this );
883 
884     if ( ImplIsInListAction_Lock() )
885     {
886         OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
887         return sal_False;
888     }
889 
890     if ( m_pData->pActUndoArray->nCurUndoAction >= m_pData->pActUndoArray->aUndoActions.size() )
891     {
892         OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" );
893         return sal_False;
894     }
895 
896     SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction;
897     const String sActionComment = pAction->GetComment();
898     try
899     {
900         // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
901         // nowadays ...
902         aGuard.clear();
903         if ( i_contextOrNull != NULL )
904             pAction->RedoWithContext( *i_contextOrNull );
905         else
906             pAction->Redo();
907         aGuard.reset();
908     }
909     catch( ... )
910     {
911         aGuard.reset();
912 
913         // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
914         // we still find pAction in our current Undo array
915         size_t nCurAction = 0;
916         while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
917         {
918             if ( m_pData->pActUndoArray->aUndoActions[ nCurAction ].pAction == pAction )
919             {
920                 // the Undo action is still there ...
921                 // assume the error is a permanent failure, and clear the Undo stack
922                 ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
923                 throw;
924             }
925             ++nCurAction;
926         }
927         OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
928         throw;
929     }
930 
931     aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
932 
933 	return sal_True;
934 }
935 
936 //------------------------------------------------------------------------
937 
GetRepeatActionCount() const938 size_t SfxUndoManager::GetRepeatActionCount() const
939 {
940     UndoManagerGuard aGuard( *m_pData );
941 	return m_pData->pActUndoArray->aUndoActions.size();
942 }
943 
944 //------------------------------------------------------------------------
945 
GetRepeatActionComment(SfxRepeatTarget & rTarget) const946 XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) const
947 {
948     UndoManagerGuard aGuard( *m_pData );
949 	return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction
950 		->GetRepeatComment(rTarget);
951 }
952 
953 //------------------------------------------------------------------------
954 
Repeat(SfxRepeatTarget & rTarget)955 sal_Bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
956 {
957     UndoManagerGuard aGuard( *m_pData );
958 	if ( !m_pData->pActUndoArray->aUndoActions.empty() )
959 	{
960         SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction;
961         aGuard.clear();
962         if ( pAction->CanRepeat( rTarget ) )
963             pAction->Repeat( rTarget );
964 		return sal_True;
965 	}
966 
967 	return sal_False;
968 }
969 
970 //------------------------------------------------------------------------
971 
CanRepeat(SfxRepeatTarget & rTarget) const972 sal_Bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
973 {
974     UndoManagerGuard aGuard( *m_pData );
975 	if ( !m_pData->pActUndoArray->aUndoActions.empty() )
976 	{
977 		size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1;
978 		return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
979 	}
980 	return sal_False;
981 }
982 
983 //------------------------------------------------------------------------
984 
AddUndoListener(SfxUndoListener & i_listener)985 void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
986 {
987     UndoManagerGuard aGuard( *m_pData );
988     m_pData->aListeners.push_back( &i_listener );
989 }
990 
991 //------------------------------------------------------------------------
992 
RemoveUndoListener(SfxUndoListener & i_listener)993 void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
994 {
995     UndoManagerGuard aGuard( *m_pData );
996     for (   UndoListeners::iterator lookup = m_pData->aListeners.begin();
997             lookup != m_pData->aListeners.end();
998             ++lookup
999         )
1000     {
1001         if ( (*lookup) == &i_listener )
1002         {
1003             m_pData->aListeners.erase( lookup );
1004             break;
1005         }
1006     }
1007 }
1008 
1009 //------------------------------------------------------------------------
1010 
EnterListAction(const XubString & rComment,const XubString & rRepeatComment,sal_uInt16 nId)1011 void SfxUndoManager::EnterListAction(
1012 	const XubString& rComment, const XubString &rRepeatComment, sal_uInt16 nId )
1013 
1014 /*	[Beschreibung]
1015 
1016 	Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles.
1017 */
1018 
1019 {
1020     UndoManagerGuard aGuard( *m_pData );
1021 
1022     if( !ImplIsUndoEnabled_Lock() )
1023 		return;
1024 
1025 	if ( !m_pData->pUndoArray->nMaxUndoActions )
1026 		return;
1027 
1028 	m_pData->pFatherUndoArray = m_pData->pActUndoArray;
1029 	SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
1030     OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) );
1031         // expected to succeed: all conditions under which it could fail should have been checked already
1032 	m_pData->pActUndoArray = pAction;
1033 
1034     // notification
1035     aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
1036 }
1037 
1038 //------------------------------------------------------------------------
1039 
IsInListAction() const1040 bool SfxUndoManager::IsInListAction() const
1041 {
1042     UndoManagerGuard aGuard( *m_pData );
1043     return ImplIsInListAction_Lock();
1044 }
1045 
1046 //------------------------------------------------------------------------
1047 
ImplIsInListAction_Lock() const1048 bool SfxUndoManager::ImplIsInListAction_Lock() const
1049 {
1050     return ( m_pData->pActUndoArray != m_pData->pUndoArray );
1051 }
1052 
1053 //------------------------------------------------------------------------
1054 
GetListActionDepth() const1055 size_t SfxUndoManager::GetListActionDepth() const
1056 {
1057     UndoManagerGuard aGuard( *m_pData );
1058     size_t nDepth(0);
1059 
1060     SfxUndoArray* pLookup( m_pData->pActUndoArray );
1061     while ( pLookup != m_pData->pUndoArray )
1062     {
1063         pLookup = pLookup->pFatherUndoArray;
1064         ++nDepth;
1065     }
1066 
1067     return nDepth;
1068 }
1069 
1070 //------------------------------------------------------------------------
1071 
LeaveListAction()1072 size_t SfxUndoManager::LeaveListAction()
1073 {
1074     UndoManagerGuard aGuard( *m_pData );
1075     size_t nCount = ImplLeaveListAction( false, aGuard );
1076 
1077     if ( m_pData->mbClearUntilTopLevel )
1078     {
1079         ImplClearCurrentLevel_NoNotify( aGuard );
1080         if ( !ImplIsInListAction_Lock() )
1081         {
1082             m_pData->mbClearUntilTopLevel = false;
1083             aGuard.scheduleNotification( &SfxUndoListener::cleared );
1084         }
1085         nCount = 0;
1086     }
1087 
1088     return nCount;
1089 }
1090 
1091 //------------------------------------------------------------------------
1092 
LeaveAndMergeListAction()1093 size_t SfxUndoManager::LeaveAndMergeListAction()
1094 {
1095     UndoManagerGuard aGuard( *m_pData );
1096     return ImplLeaveListAction( true, aGuard );
1097 }
1098 
1099 //------------------------------------------------------------------------
1100 
ImplLeaveListAction(const bool i_merge,UndoManagerGuard & i_guard)1101 size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
1102 {
1103     if ( !ImplIsUndoEnabled_Lock() )
1104 		return 0;
1105 
1106 	if ( !m_pData->pUndoArray->nMaxUndoActions )
1107 		return 0;
1108 
1109 	if( !ImplIsInListAction_Lock() )
1110 	{
1111 		DBG_ERROR( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
1112 		return 0;
1113 	}
1114 
1115 	DBG_ASSERT( m_pData->pActUndoArray->pFatherUndoArray, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" );
1116 
1117     // the array/level which we're about to leave
1118 	SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray;
1119     // one step up
1120 	m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray;
1121 
1122 	// If no undo actions were added to the list, delete the list action
1123     const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
1124 	if ( nListActionElements == 0 )
1125 	{
1126 	    SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1127 		m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
1128         i_guard.markForDeletion( pCurrentAction );
1129 
1130         i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
1131         return 0;
1132     }
1133 
1134     // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1135     // the redo stack
1136     ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
1137 
1138     SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1139 	SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
1140     ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
1141 
1142     if ( i_merge )
1143     {
1144         // merge the list action with its predecessor on the same level
1145         OSL_ENSURE( m_pData->pActUndoArray->nCurUndoAction > 1,
1146             "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
1147         if ( m_pData->pActUndoArray->nCurUndoAction > 1 )
1148         {
1149             SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction;
1150             m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 );
1151             --m_pData->pActUndoArray->nCurUndoAction;
1152             pListAction->aUndoActions.Insert( pPreviousAction, 0 );
1153             ++pListAction->nCurUndoAction;
1154 
1155             pListAction->SetComment( pPreviousAction->GetComment() );
1156         }
1157     }
1158 
1159     // if the undo array has no comment, try to get it from its children
1160 	if ( pListAction->GetComment().Len() == 0 )
1161 	{
1162 		for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
1163 		{
1164 			if( pListAction->aUndoActions[n].pAction->GetComment().Len() )
1165 			{
1166 				pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
1167 				break;
1168 			}
1169 		}
1170 	}
1171 
1172     // notify listeners
1173     i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
1174 
1175     // outta here
1176     return nListActionElements;
1177 }
1178 
1179 //------------------------------------------------------------------------
MarkTopUndoAction()1180 UndoStackMark SfxUndoManager::MarkTopUndoAction()
1181 {
1182     UndoManagerGuard aGuard( *m_pData );
1183 
1184     OSL_ENSURE( !IsInListAction(),
1185             "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
1186     OSL_ENSURE((m_pData->mnMarks + 1) < (m_pData->mnEmptyMark - 1),
1187             "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
1188 
1189     size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction;
1190     if (0 == nActionPos)
1191     {
1192         --m_pData->mnEmptyMark;
1193         return m_pData->mnEmptyMark;
1194     }
1195 
1196     m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
1197             ++m_pData->mnMarks );
1198     return m_pData->mnMarks;
1199 }
1200 
1201 //------------------------------------------------------------------------
RemoveMark(UndoStackMark const i_mark)1202 void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
1203 {
1204     UndoManagerGuard aGuard( *m_pData );
1205 
1206     if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
1207     {
1208         return; // nothing to remove
1209     }
1210     else if (i_mark == m_pData->mnEmptyMark)
1211     {
1212         --m_pData->mnEmptyMark; // never returned from MarkTop => invalid
1213         return;
1214     }
1215 
1216     for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i )
1217     {
1218         MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i];
1219         for (   ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
1220                 markPos != rAction.aMarks.end();
1221                 ++markPos
1222             )
1223         {
1224             if ( *markPos == i_mark )
1225             {
1226                 rAction.aMarks.erase( markPos );
1227                 return;
1228             }
1229         }
1230     }
1231     OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" );
1232         // TODO: this might be too offensive. There are situations where we implicitly remove marks
1233         // without our clients, in particular the client which created the mark, having a chance to know
1234         // about this.
1235 }
1236 
1237 //------------------------------------------------------------------------
HasTopUndoActionMark(UndoStackMark const i_mark)1238 bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
1239 {
1240     UndoManagerGuard aGuard( *m_pData );
1241 
1242     size_t nActionPos = m_pData->pUndoArray->nCurUndoAction;
1243     if ( nActionPos == 0 )
1244     {
1245         return (i_mark == m_pData->mnEmptyMark);
1246     }
1247 
1248     const MarkedUndoAction& rAction =
1249             m_pData->pUndoArray->aUndoActions[ nActionPos-1 ];
1250     for (   ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
1251             markPos != rAction.aMarks.end();
1252             ++markPos
1253         )
1254     {
1255         if ( *markPos == i_mark )
1256             return true;
1257     }
1258 
1259     return false;
1260 }
1261 
1262 //------------------------------------------------------------------------
1263 
RemoveOldestUndoActions(size_t const i_count)1264 void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count )
1265 {
1266     UndoManagerGuard aGuard( *m_pData );
1267 
1268     size_t nActionsToRemove = i_count;
1269     while ( nActionsToRemove )
1270     {
1271         SfxUndoAction* pActionToRemove = m_pData->pUndoArray->aUndoActions[0].pAction;
1272 
1273         if ( IsInListAction() && ( m_pData->pUndoArray->nCurUndoAction == 1 ) )
1274         {
1275             OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" );
1276             return;
1277         }
1278 
1279         aGuard.markForDeletion( pActionToRemove );
1280         m_pData->pUndoArray->aUndoActions.Remove( 0 );
1281         --m_pData->pUndoArray->nCurUndoAction;
1282         --nActionsToRemove;
1283     }
1284 }
1285 
1286 //------------------------------------------------------------------------
1287 
GetId() const1288 sal_uInt16 SfxListUndoAction::GetId() const
1289 {
1290 	return nId;
1291 }
1292 
1293 //------------------------------------------------------------------------
1294 
GetComment() const1295 XubString SfxListUndoAction::GetComment() const
1296 {
1297 	return aComment;
1298 }
1299 
1300 //------------------------------------------------------------------------
1301 
SetComment(const UniString & rComment)1302 void SfxListUndoAction::SetComment( const UniString& rComment )
1303 {
1304 	aComment = rComment;
1305 }
1306 
1307 //------------------------------------------------------------------------
1308 
GetRepeatComment(SfxRepeatTarget &) const1309 XubString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
1310 {
1311 	return aRepeatComment;
1312 }
1313 
1314 
1315 //------------------------------------------------------------------------
1316 
SfxListUndoAction(const XubString & rComment,const XubString rRepeatComment,sal_uInt16 Id,SfxUndoArray * pFather)1317 SfxListUndoAction::SfxListUndoAction
1318 (
1319 	const XubString &rComment,
1320 	const XubString rRepeatComment,
1321 	sal_uInt16 Id,
1322 	SfxUndoArray *pFather
1323 )
1324 : nId(Id), aComment(rComment), aRepeatComment(rRepeatComment)
1325 {
1326 	pFatherUndoArray = pFather;
1327 	nMaxUndoActions = USHRT_MAX;
1328 }
1329 
1330 //------------------------------------------------------------------------
1331 
Undo()1332 void SfxListUndoAction::Undo()
1333 {
1334 	for(size_t i=nCurUndoAction;i>0;)
1335 		aUndoActions[--i].pAction->Undo();
1336 	nCurUndoAction=0;
1337 }
1338 
1339 //------------------------------------------------------------------------
1340 
UndoWithContext(SfxUndoContext & i_context)1341 void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1342 {
1343 	for(size_t i=nCurUndoAction;i>0;)
1344 		aUndoActions[--i].pAction->UndoWithContext( i_context );
1345 	nCurUndoAction=0;
1346 }
1347 
1348 //------------------------------------------------------------------------
1349 
Redo()1350 void SfxListUndoAction::Redo()
1351 {
1352 	for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1353 		aUndoActions[i].pAction->Redo();
1354 	nCurUndoAction = aUndoActions.size();
1355 }
1356 
1357 //------------------------------------------------------------------------
1358 
RedoWithContext(SfxUndoContext & i_context)1359 void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
1360 {
1361 	for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1362 		aUndoActions[i].pAction->RedoWithContext( i_context );
1363 	nCurUndoAction = aUndoActions.size();
1364 }
1365 
1366 //------------------------------------------------------------------------
1367 
Repeat(SfxRepeatTarget & rTarget)1368 void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
1369 {
1370 	for(size_t i=0;i<nCurUndoAction;i++)
1371 		aUndoActions[i].pAction->Repeat(rTarget);
1372 }
1373 
1374 //------------------------------------------------------------------------
1375 
CanRepeat(SfxRepeatTarget & r) const1376 sal_Bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r)  const
1377 {
1378 	for(size_t i=0;i<nCurUndoAction;i++)
1379 		if(!aUndoActions[i].pAction->CanRepeat(r))
1380 			return sal_False;
1381 	return sal_True;
1382 }
1383 
1384 //------------------------------------------------------------------------
1385 
Merge(SfxUndoAction * pNextAction)1386 sal_Bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
1387 {
1388 	return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction );
1389 }
1390 
1391 //------------------------------------------------------------------------
1392 
SfxLinkUndoAction(::svl::IUndoManager * pManager)1393 SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager)
1394 /*	[Beschreibung]
1395 
1396 	Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt.
1397 	Holt sich als zugehoerige Action des weiteren UndoManagers dessen
1398 	aktuelle Action.
1399 */
1400 
1401 {
1402 	pUndoManager = pManager;
1403     SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager );
1404     ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" );
1405         // yes, this cast is dirty. But reaching into the the SfxUndoManager's implementation,
1406         // directly accessing its internal stack, and tampering with an action on that stack
1407         // is dirty, too.
1408 	if ( pManager->GetMaxUndoActionCount() )
1409 	{
1410 		size_t nPos = pManager->GetUndoActionCount()-1;
1411 		pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction;
1412 		pAction->SetLinkToSfxLinkUndoAction(this);
1413 	}
1414 	else
1415 		pAction = 0;
1416 }
1417 
1418 //------------------------------------------------------------------------
1419 
Undo()1420 void SfxLinkUndoAction::Undo()
1421 {
1422 	if ( pAction )
1423 		pUndoManager->Undo();
1424 }
1425 
1426 //------------------------------------------------------------------------
1427 
Redo()1428 void SfxLinkUndoAction::Redo()
1429 {
1430 	if ( pAction )
1431 		pUndoManager->Redo();
1432 }
1433 
1434 //------------------------------------------------------------------------
1435 
1436 
CanRepeat(SfxRepeatTarget & r) const1437 sal_Bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
1438 {
1439 	return pAction && pAction->CanRepeat(r);
1440 }
1441 
1442 
1443 //------------------------------------------------------------------------
1444 
1445 
Repeat(SfxRepeatTarget & r)1446 void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r)
1447 {
1448 	if ( pAction && pAction->CanRepeat( r ) )
1449 	    pAction->Repeat( r );
1450 }
1451 
1452 
1453 //------------------------------------------------------------------------
1454 
GetComment() const1455 XubString SfxLinkUndoAction::GetComment() const
1456 {
1457 	if ( pAction )
1458 		return pAction->GetComment();
1459 	else
1460 		return XubString();
1461 }
1462 
1463 
1464 //------------------------------------------------------------------------
1465 
GetRepeatComment(SfxRepeatTarget & r) const1466 XubString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
1467 {
1468 	if ( pAction )
1469 		return pAction->GetRepeatComment(r);
1470 	else
1471 		return XubString();
1472 }
1473 
1474 //------------------------------------------------------------------------
1475 
~SfxLinkUndoAction()1476 SfxLinkUndoAction::~SfxLinkUndoAction()
1477 {
1478 	if( pAction )
1479 		pAction->SetLinkToSfxLinkUndoAction(0);
1480 }
1481 
1482 //------------------------------------------------------------------------
1483 
LinkedSfxUndoActionDestructed(const SfxUndoAction & rCandidate)1484 void SfxLinkUndoAction::LinkedSfxUndoActionDestructed(const SfxUndoAction& rCandidate)
1485 {
1486     OSL_ENSURE(0 != pAction, "OOps, we have no linked SfxUndoAction (!)");
1487     OSL_ENSURE(pAction == &rCandidate, "OOps, the destroyed and linked UndoActions differ (!)");
1488     (void)rCandidate;
1489     pAction = 0;
1490 }
1491 
1492 //------------------------------------------------------------------------
1493 
~SfxUndoArray()1494 SfxUndoArray::~SfxUndoArray()
1495 {
1496 	while ( !aUndoActions.empty() )
1497 	{
1498 		SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
1499 		aUndoActions.Remove( aUndoActions.size() - 1 );
1500 		delete pAction;
1501 	}
1502 }
1503 
1504 
GetId() const1505 sal_uInt16 SfxLinkUndoAction::GetId() const
1506 {
1507       return pAction ? pAction->GetId() : 0;
1508 }
1509