xref: /trunk/main/svl/source/undo/undo.cxx (revision 7950f2af)
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 
640             if(m_pData->pActUndoArray->nCurUndoAction)
641             {
642                 --m_pData->pActUndoArray->nCurUndoAction;
643             }
644             else
645             {
646                 OSL_ENSURE(false, "CurrentUndoAction going negative (!)");
647             }
648 		}
649     }
650 
651 	// append new action
652 	m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ );
653     return true;
654 }
655 
656 //------------------------------------------------------------------------
657 
AddUndoAction(SfxUndoAction * pAction,sal_Bool bTryMerge)658 void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerge )
659 {
660     UndoManagerGuard aGuard( *m_pData );
661 
662     // add
663     if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
664     {
665         // notify listeners
666         aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
667     }
668 }
669 
670 //------------------------------------------------------------------------
671 
GetUndoActionCount(bool const i_currentLevel) const672 size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
673 {
674     UndoManagerGuard aGuard( *m_pData );
675     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
676 	return pUndoArray->nCurUndoAction;
677 }
678 
679 //------------------------------------------------------------------------
680 
GetUndoActionComment(size_t nNo,bool const i_currentLevel) const681 XubString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
682 {
683     UndoManagerGuard aGuard( *m_pData );
684 
685     String sComment;
686     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
687     DBG_ASSERT( nNo < pUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" );
688 	if( nNo < pUndoArray->nCurUndoAction )
689 	{
690 		sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
691 	}
692     return sComment;
693 }
694 
695 //------------------------------------------------------------------------
696 
GetUndoActionId() const697 sal_uInt16 SfxUndoManager::GetUndoActionId() const
698 {
699     UndoManagerGuard aGuard( *m_pData );
700 
701     DBG_ASSERT( m_pData->pActUndoArray->nCurUndoAction > 0, "svl::SfxUndoManager::GetUndoActionId(), illegal id!" );
702 	if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
703         return 0;
704 	return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction->GetId();
705 }
706 
707 //------------------------------------------------------------------------
708 
GetUndoAction(size_t nNo) const709 SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const
710 {
711     UndoManagerGuard aGuard( *m_pData );
712 
713 	DBG_ASSERT( nNo < m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
714 	if( nNo >= m_pData->pActUndoArray->nCurUndoAction )
715         return NULL;
716 	return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1-nNo].pAction;
717 }
718 
719 //------------------------------------------------------------------------
720 
721 /** clears the redo stack and removes the top undo action */
RemoveLastUndoAction()722 void SfxUndoManager::RemoveLastUndoAction()
723 {
724     UndoManagerGuard aGuard( *m_pData );
725 
726 	ENSURE_OR_RETURN_VOID( m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
727 
728     m_pData->pActUndoArray->nCurUndoAction--;
729 
730 	// delete redo-actions and top action
731 	for ( size_t nPos = m_pData->pActUndoArray->aUndoActions.size(); nPos > m_pData->pActUndoArray->nCurUndoAction; --nPos )
732     {
733         aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[nPos-1].pAction );
734     }
735 
736 	m_pData->pActUndoArray->aUndoActions.Remove(
737 		m_pData->pActUndoArray->nCurUndoAction,
738 		m_pData->pActUndoArray->aUndoActions.size() - m_pData->pActUndoArray->nCurUndoAction );
739 }
740 
741 //------------------------------------------------------------------------
742 
IsDoing() const743 bool SfxUndoManager::IsDoing() const
744 {
745     UndoManagerGuard aGuard( *m_pData );
746     return m_pData->mbDoing;
747 }
748 
749 //------------------------------------------------------------------------
750 
Undo()751 sal_Bool SfxUndoManager::Undo()
752 {
753     return ImplUndo( NULL );
754 }
755 
756 //------------------------------------------------------------------------
757 
UndoWithContext(SfxUndoContext & i_context)758 sal_Bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
759 {
760     return ImplUndo( &i_context );
761 }
762 
763 //------------------------------------------------------------------------
764 
ImplUndo(SfxUndoContext * i_contextOrNull)765 sal_Bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
766 {
767     UndoManagerGuard aGuard( *m_pData );
768     OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
769 
770     ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
771     LockGuard aLockGuard( *this );
772 
773     if ( ImplIsInListAction_Lock() )
774     {
775         OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" );
776         return sal_False;
777     }
778 
779     if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
780     {
781         OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" );
782         return sal_False;
783     }
784 
785     SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ].pAction;
786     const String sActionComment = pAction->GetComment();
787     try
788     {
789         // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
790         // nowadays ...
791         aGuard.clear();
792         if ( i_contextOrNull != NULL )
793             pAction->UndoWithContext( *i_contextOrNull );
794         else
795             pAction->Undo();
796         aGuard.reset();
797     }
798     catch( ... )
799     {
800         aGuard.reset();
801 
802         // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
803         // we still find pAction in our current Undo array
804         size_t nCurAction = 0;
805         while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
806         {
807             if ( m_pData->pActUndoArray->aUndoActions[ nCurAction++ ].pAction == pAction )
808             {
809                 // the Undo action is still there ...
810                 // assume the error is a permanent failure, and clear the Undo stack
811                 ImplClearUndo( aGuard );
812                 throw;
813             }
814         }
815         OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
816         throw;
817     }
818 
819     aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
820 
821     return sal_True;
822 }
823 
824 //------------------------------------------------------------------------
825 
GetRedoActionCount(bool const i_currentLevel) const826 size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
827 {
828     UndoManagerGuard aGuard( *m_pData );
829     return ImplGetRedoActionCount_Lock( i_currentLevel );
830 }
831 
832 //------------------------------------------------------------------------
833 
ImplGetRedoActionCount_Lock(bool const i_currentLevel) const834 size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
835 {
836     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
837 	return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction;
838 }
839 
840 //------------------------------------------------------------------------
841 
GetRedoAction(size_t nNo,bool const i_currentLevel) const842 SfxUndoAction* SfxUndoManager::GetRedoAction( size_t nNo, bool const i_currentLevel ) const
843 {
844     UndoManagerGuard aGuard( *m_pData );
845 
846     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
847     if ( (pUndoArray->nCurUndoAction + nNo) > pUndoArray->aUndoActions.size() )
848     {
849         return NULL;
850     }
851     return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction;
852 }
853 
854 //------------------------------------------------------------------------
855 
GetRedoActionComment(size_t nNo,bool const i_currentLevel) const856 XubString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
857 {
858     String sComment;
859     UndoManagerGuard aGuard( *m_pData );
860     const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
861     if ( (pUndoArray->nCurUndoAction + nNo) < pUndoArray->aUndoActions.size() )
862     {
863         sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
864     }
865     return sComment;
866 }
867 
868 //------------------------------------------------------------------------
869 
Redo()870 sal_Bool SfxUndoManager::Redo()
871 {
872     return ImplRedo( NULL );
873 }
874 
875 //------------------------------------------------------------------------
876 
RedoWithContext(SfxUndoContext & i_context)877 sal_Bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
878 {
879     return ImplRedo( &i_context );
880 }
881 
882 //------------------------------------------------------------------------
883 
ImplRedo(SfxUndoContext * i_contextOrNull)884 sal_Bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
885 {
886     UndoManagerGuard aGuard( *m_pData );
887     OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
888 
889     ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
890     LockGuard aLockGuard( *this );
891 
892     if ( ImplIsInListAction_Lock() )
893     {
894         OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
895         return sal_False;
896     }
897 
898     if ( m_pData->pActUndoArray->nCurUndoAction >= m_pData->pActUndoArray->aUndoActions.size() )
899     {
900         OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" );
901         return sal_False;
902     }
903 
904     SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction;
905     const String sActionComment = pAction->GetComment();
906     try
907     {
908         // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
909         // nowadays ...
910         aGuard.clear();
911         if ( i_contextOrNull != NULL )
912             pAction->RedoWithContext( *i_contextOrNull );
913         else
914             pAction->Redo();
915         aGuard.reset();
916     }
917     catch( ... )
918     {
919         aGuard.reset();
920 
921         // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
922         // we still find pAction in our current Undo array
923         size_t nCurAction = 0;
924         while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
925         {
926             if ( m_pData->pActUndoArray->aUndoActions[ nCurAction ].pAction == pAction )
927             {
928                 // the Undo action is still there ...
929                 // assume the error is a permanent failure, and clear the Undo stack
930                 ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
931                 throw;
932             }
933             ++nCurAction;
934         }
935         OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
936         throw;
937     }
938 
939     aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
940 
941 	return sal_True;
942 }
943 
944 //------------------------------------------------------------------------
945 
GetRepeatActionCount() const946 size_t SfxUndoManager::GetRepeatActionCount() const
947 {
948     UndoManagerGuard aGuard( *m_pData );
949 	return m_pData->pActUndoArray->aUndoActions.size();
950 }
951 
952 //------------------------------------------------------------------------
953 
GetRepeatActionComment(SfxRepeatTarget & rTarget) const954 XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) const
955 {
956     UndoManagerGuard aGuard( *m_pData );
957 	return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction
958 		->GetRepeatComment(rTarget);
959 }
960 
961 //------------------------------------------------------------------------
962 
Repeat(SfxRepeatTarget & rTarget)963 sal_Bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
964 {
965     UndoManagerGuard aGuard( *m_pData );
966 	if ( !m_pData->pActUndoArray->aUndoActions.empty() )
967 	{
968         SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction;
969         aGuard.clear();
970         if ( pAction->CanRepeat( rTarget ) )
971             pAction->Repeat( rTarget );
972 		return sal_True;
973 	}
974 
975 	return sal_False;
976 }
977 
978 //------------------------------------------------------------------------
979 
CanRepeat(SfxRepeatTarget & rTarget) const980 sal_Bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
981 {
982     UndoManagerGuard aGuard( *m_pData );
983 	if ( !m_pData->pActUndoArray->aUndoActions.empty() )
984 	{
985 		size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1;
986 		return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
987 	}
988 	return sal_False;
989 }
990 
991 //------------------------------------------------------------------------
992 
AddUndoListener(SfxUndoListener & i_listener)993 void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
994 {
995     UndoManagerGuard aGuard( *m_pData );
996     m_pData->aListeners.push_back( &i_listener );
997 }
998 
999 //------------------------------------------------------------------------
1000 
RemoveUndoListener(SfxUndoListener & i_listener)1001 void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
1002 {
1003     UndoManagerGuard aGuard( *m_pData );
1004     for (   UndoListeners::iterator lookup = m_pData->aListeners.begin();
1005             lookup != m_pData->aListeners.end();
1006             ++lookup
1007         )
1008     {
1009         if ( (*lookup) == &i_listener )
1010         {
1011             m_pData->aListeners.erase( lookup );
1012             break;
1013         }
1014     }
1015 }
1016 
1017 //------------------------------------------------------------------------
1018 
EnterListAction(const XubString & rComment,const XubString & rRepeatComment,sal_uInt16 nId)1019 void SfxUndoManager::EnterListAction(
1020 	const XubString& rComment, const XubString &rRepeatComment, sal_uInt16 nId )
1021 
1022 /*	[Beschreibung]
1023 
1024 	Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles.
1025 */
1026 
1027 {
1028     UndoManagerGuard aGuard( *m_pData );
1029 
1030     if( !ImplIsUndoEnabled_Lock() )
1031 		return;
1032 
1033 	if ( !m_pData->pUndoArray->nMaxUndoActions )
1034 		return;
1035 
1036 	m_pData->pFatherUndoArray = m_pData->pActUndoArray;
1037 	SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
1038     OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) );
1039         // expected to succeed: all conditions under which it could fail should have been checked already
1040 	m_pData->pActUndoArray = pAction;
1041 
1042     // notification
1043     aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
1044 }
1045 
1046 //------------------------------------------------------------------------
1047 
IsInListAction() const1048 bool SfxUndoManager::IsInListAction() const
1049 {
1050     UndoManagerGuard aGuard( *m_pData );
1051     return ImplIsInListAction_Lock();
1052 }
1053 
1054 //------------------------------------------------------------------------
1055 
ImplIsInListAction_Lock() const1056 bool SfxUndoManager::ImplIsInListAction_Lock() const
1057 {
1058     return ( m_pData->pActUndoArray != m_pData->pUndoArray );
1059 }
1060 
1061 //------------------------------------------------------------------------
1062 
GetListActionDepth() const1063 size_t SfxUndoManager::GetListActionDepth() const
1064 {
1065     UndoManagerGuard aGuard( *m_pData );
1066     size_t nDepth(0);
1067 
1068     SfxUndoArray* pLookup( m_pData->pActUndoArray );
1069     while ( pLookup != m_pData->pUndoArray )
1070     {
1071         pLookup = pLookup->pFatherUndoArray;
1072         ++nDepth;
1073     }
1074 
1075     return nDepth;
1076 }
1077 
1078 //------------------------------------------------------------------------
1079 
LeaveListAction()1080 size_t SfxUndoManager::LeaveListAction()
1081 {
1082     UndoManagerGuard aGuard( *m_pData );
1083     size_t nCount = ImplLeaveListAction( false, aGuard );
1084 
1085     if ( m_pData->mbClearUntilTopLevel )
1086     {
1087         ImplClearCurrentLevel_NoNotify( aGuard );
1088         if ( !ImplIsInListAction_Lock() )
1089         {
1090             m_pData->mbClearUntilTopLevel = false;
1091             aGuard.scheduleNotification( &SfxUndoListener::cleared );
1092         }
1093         nCount = 0;
1094     }
1095 
1096     return nCount;
1097 }
1098 
1099 //------------------------------------------------------------------------
1100 
LeaveAndMergeListAction()1101 size_t SfxUndoManager::LeaveAndMergeListAction()
1102 {
1103     UndoManagerGuard aGuard( *m_pData );
1104     return ImplLeaveListAction( true, aGuard );
1105 }
1106 
1107 //------------------------------------------------------------------------
1108 
ImplLeaveListAction(const bool i_merge,UndoManagerGuard & i_guard)1109 size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
1110 {
1111     if ( !ImplIsUndoEnabled_Lock() )
1112 		return 0;
1113 
1114 	if ( !m_pData->pUndoArray->nMaxUndoActions )
1115 		return 0;
1116 
1117 	if( !ImplIsInListAction_Lock() )
1118 	{
1119 		DBG_ERROR( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
1120 		return 0;
1121 	}
1122 
1123 	DBG_ASSERT( m_pData->pActUndoArray->pFatherUndoArray, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" );
1124 
1125     // the array/level which we're about to leave
1126 	SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray;
1127     // one step up
1128 	m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray;
1129 
1130 	// If no undo actions were added to the list, delete the list action
1131     const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
1132 	if ( nListActionElements == 0 )
1133 	{
1134 	    SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1135 		m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
1136         i_guard.markForDeletion( pCurrentAction );
1137 
1138         i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
1139         return 0;
1140     }
1141 
1142     // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1143     // the redo stack
1144     ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
1145 
1146     SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1147 	SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
1148     ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
1149 
1150     if ( i_merge )
1151     {
1152         // merge the list action with its predecessor on the same level
1153         OSL_ENSURE( m_pData->pActUndoArray->nCurUndoAction > 1,
1154             "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
1155         if ( m_pData->pActUndoArray->nCurUndoAction > 1 )
1156         {
1157             SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction;
1158             m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 );
1159             --m_pData->pActUndoArray->nCurUndoAction;
1160             pListAction->aUndoActions.Insert( pPreviousAction, 0 );
1161             ++pListAction->nCurUndoAction;
1162 
1163             pListAction->SetComment( pPreviousAction->GetComment() );
1164         }
1165     }
1166 
1167     // if the undo array has no comment, try to get it from its children
1168 	if ( pListAction->GetComment().Len() == 0 )
1169 	{
1170 		for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
1171 		{
1172 			if( pListAction->aUndoActions[n].pAction->GetComment().Len() )
1173 			{
1174 				pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
1175 				break;
1176 			}
1177 		}
1178 	}
1179 
1180     // notify listeners
1181     i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
1182 
1183     // outta here
1184     return nListActionElements;
1185 }
1186 
1187 //------------------------------------------------------------------------
MarkTopUndoAction()1188 UndoStackMark SfxUndoManager::MarkTopUndoAction()
1189 {
1190     UndoManagerGuard aGuard( *m_pData );
1191 
1192     OSL_ENSURE( !IsInListAction(),
1193             "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
1194     OSL_ENSURE((m_pData->mnMarks + 1) < (m_pData->mnEmptyMark - 1),
1195             "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
1196 
1197     size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction;
1198     if (0 == nActionPos)
1199     {
1200         --m_pData->mnEmptyMark;
1201         return m_pData->mnEmptyMark;
1202     }
1203 
1204     m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
1205             ++m_pData->mnMarks );
1206     return m_pData->mnMarks;
1207 }
1208 
1209 //------------------------------------------------------------------------
RemoveMark(UndoStackMark const i_mark)1210 void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
1211 {
1212     UndoManagerGuard aGuard( *m_pData );
1213 
1214     if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
1215     {
1216         return; // nothing to remove
1217     }
1218     else if (i_mark == m_pData->mnEmptyMark)
1219     {
1220         --m_pData->mnEmptyMark; // never returned from MarkTop => invalid
1221         return;
1222     }
1223 
1224     for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i )
1225     {
1226         MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i];
1227         for (   ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
1228                 markPos != rAction.aMarks.end();
1229                 ++markPos
1230             )
1231         {
1232             if ( *markPos == i_mark )
1233             {
1234                 rAction.aMarks.erase( markPos );
1235                 return;
1236             }
1237         }
1238     }
1239     OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" );
1240         // TODO: this might be too offensive. There are situations where we implicitly remove marks
1241         // without our clients, in particular the client which created the mark, having a chance to know
1242         // about this.
1243 }
1244 
1245 //------------------------------------------------------------------------
HasTopUndoActionMark(UndoStackMark const i_mark)1246 bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
1247 {
1248     UndoManagerGuard aGuard( *m_pData );
1249 
1250     size_t nActionPos = m_pData->pUndoArray->nCurUndoAction;
1251     if ( nActionPos == 0 )
1252     {
1253         return (i_mark == m_pData->mnEmptyMark);
1254     }
1255 
1256     const MarkedUndoAction& rAction =
1257             m_pData->pUndoArray->aUndoActions[ nActionPos-1 ];
1258     for (   ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
1259             markPos != rAction.aMarks.end();
1260             ++markPos
1261         )
1262     {
1263         if ( *markPos == i_mark )
1264             return true;
1265     }
1266 
1267     return false;
1268 }
1269 
1270 //------------------------------------------------------------------------
1271 
RemoveOldestUndoActions(size_t const i_count)1272 void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count )
1273 {
1274     UndoManagerGuard aGuard( *m_pData );
1275 
1276     size_t nActionsToRemove = i_count;
1277     while ( nActionsToRemove )
1278     {
1279         SfxUndoAction* pActionToRemove = m_pData->pUndoArray->aUndoActions[0].pAction;
1280 
1281         if ( IsInListAction() && ( m_pData->pUndoArray->nCurUndoAction == 1 ) )
1282         {
1283             OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" );
1284             return;
1285         }
1286 
1287         aGuard.markForDeletion( pActionToRemove );
1288         m_pData->pUndoArray->aUndoActions.Remove( 0 );
1289         --m_pData->pUndoArray->nCurUndoAction;
1290         --nActionsToRemove;
1291     }
1292 }
1293 
1294 //------------------------------------------------------------------------
1295 
GetId() const1296 sal_uInt16 SfxListUndoAction::GetId() const
1297 {
1298 	return nId;
1299 }
1300 
1301 //------------------------------------------------------------------------
1302 
GetComment() const1303 XubString SfxListUndoAction::GetComment() const
1304 {
1305 	return aComment;
1306 }
1307 
1308 //------------------------------------------------------------------------
1309 
SetComment(const UniString & rComment)1310 void SfxListUndoAction::SetComment( const UniString& rComment )
1311 {
1312 	aComment = rComment;
1313 }
1314 
1315 //------------------------------------------------------------------------
1316 
GetRepeatComment(SfxRepeatTarget &) const1317 XubString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
1318 {
1319 	return aRepeatComment;
1320 }
1321 
1322 
1323 //------------------------------------------------------------------------
1324 
SfxListUndoAction(const XubString & rComment,const XubString rRepeatComment,sal_uInt16 Id,SfxUndoArray * pFather)1325 SfxListUndoAction::SfxListUndoAction
1326 (
1327 	const XubString &rComment,
1328 	const XubString rRepeatComment,
1329 	sal_uInt16 Id,
1330 	SfxUndoArray *pFather
1331 )
1332 : nId(Id), aComment(rComment), aRepeatComment(rRepeatComment)
1333 {
1334 	pFatherUndoArray = pFather;
1335 	nMaxUndoActions = USHRT_MAX;
1336 }
1337 
1338 //------------------------------------------------------------------------
1339 
Undo()1340 void SfxListUndoAction::Undo()
1341 {
1342 	for(size_t i=nCurUndoAction;i>0;)
1343 		aUndoActions[--i].pAction->Undo();
1344 	nCurUndoAction=0;
1345 }
1346 
1347 //------------------------------------------------------------------------
1348 
UndoWithContext(SfxUndoContext & i_context)1349 void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1350 {
1351 	for(size_t i=nCurUndoAction;i>0;)
1352 		aUndoActions[--i].pAction->UndoWithContext( i_context );
1353 	nCurUndoAction=0;
1354 }
1355 
1356 //------------------------------------------------------------------------
1357 
Redo()1358 void SfxListUndoAction::Redo()
1359 {
1360 	for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1361 		aUndoActions[i].pAction->Redo();
1362 	nCurUndoAction = aUndoActions.size();
1363 }
1364 
1365 //------------------------------------------------------------------------
1366 
RedoWithContext(SfxUndoContext & i_context)1367 void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
1368 {
1369 	for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1370 		aUndoActions[i].pAction->RedoWithContext( i_context );
1371 	nCurUndoAction = aUndoActions.size();
1372 }
1373 
1374 //------------------------------------------------------------------------
1375 
Repeat(SfxRepeatTarget & rTarget)1376 void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
1377 {
1378 	for(size_t i=0;i<nCurUndoAction;i++)
1379 		aUndoActions[i].pAction->Repeat(rTarget);
1380 }
1381 
1382 //------------------------------------------------------------------------
1383 
CanRepeat(SfxRepeatTarget & r) const1384 sal_Bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r)  const
1385 {
1386 	for(size_t i=0;i<nCurUndoAction;i++)
1387 		if(!aUndoActions[i].pAction->CanRepeat(r))
1388 			return sal_False;
1389 	return sal_True;
1390 }
1391 
1392 //------------------------------------------------------------------------
1393 
Merge(SfxUndoAction * pNextAction)1394 sal_Bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
1395 {
1396 	return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction );
1397 }
1398 
1399 //------------------------------------------------------------------------
1400 
SfxLinkUndoAction(::svl::IUndoManager * pManager)1401 SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager)
1402 /*	[Beschreibung]
1403 
1404 	Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt.
1405 	Holt sich als zugehoerige Action des weiteren UndoManagers dessen
1406 	aktuelle Action.
1407 */
1408 
1409 {
1410 	pUndoManager = pManager;
1411     SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager );
1412     ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" );
1413         // yes, this cast is dirty. But reaching into the SfxUndoManager's implementation,
1414         // directly accessing its internal stack, and tampering with an action on that stack
1415         // is dirty, too.
1416 	if ( pManager->GetMaxUndoActionCount() )
1417 	{
1418 		size_t nPos = pManager->GetUndoActionCount()-1;
1419 		pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction;
1420 		pAction->SetLinkToSfxLinkUndoAction(this);
1421 	}
1422 	else
1423 		pAction = 0;
1424 }
1425 
1426 //------------------------------------------------------------------------
1427 
Undo()1428 void SfxLinkUndoAction::Undo()
1429 {
1430 	if ( pAction )
1431 		pUndoManager->Undo();
1432 }
1433 
1434 //------------------------------------------------------------------------
1435 
Redo()1436 void SfxLinkUndoAction::Redo()
1437 {
1438 	if ( pAction )
1439 		pUndoManager->Redo();
1440 }
1441 
1442 //------------------------------------------------------------------------
1443 
1444 
CanRepeat(SfxRepeatTarget & r) const1445 sal_Bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
1446 {
1447 	return pAction && pAction->CanRepeat(r);
1448 }
1449 
1450 
1451 //------------------------------------------------------------------------
1452 
1453 
Repeat(SfxRepeatTarget & r)1454 void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r)
1455 {
1456 	if ( pAction && pAction->CanRepeat( r ) )
1457 	    pAction->Repeat( r );
1458 }
1459 
1460 
1461 //------------------------------------------------------------------------
1462 
GetComment() const1463 XubString SfxLinkUndoAction::GetComment() const
1464 {
1465 	if ( pAction )
1466 		return pAction->GetComment();
1467 	else
1468 		return XubString();
1469 }
1470 
1471 
1472 //------------------------------------------------------------------------
1473 
GetRepeatComment(SfxRepeatTarget & r) const1474 XubString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
1475 {
1476 	if ( pAction )
1477 		return pAction->GetRepeatComment(r);
1478 	else
1479 		return XubString();
1480 }
1481 
1482 //------------------------------------------------------------------------
1483 
~SfxLinkUndoAction()1484 SfxLinkUndoAction::~SfxLinkUndoAction()
1485 {
1486 	if( pAction )
1487 		pAction->SetLinkToSfxLinkUndoAction(0);
1488 }
1489 
1490 //------------------------------------------------------------------------
1491 
LinkedSfxUndoActionDestructed(const SfxUndoAction & rCandidate)1492 void SfxLinkUndoAction::LinkedSfxUndoActionDestructed(const SfxUndoAction& rCandidate)
1493 {
1494     OSL_ENSURE(0 != pAction, "OOps, we have no linked SfxUndoAction (!)");
1495     OSL_ENSURE(pAction == &rCandidate, "OOps, the destroyed and linked UndoActions differ (!)");
1496     (void)rCandidate;
1497     pAction = 0;
1498 }
1499 
1500 //------------------------------------------------------------------------
1501 
~SfxUndoArray()1502 SfxUndoArray::~SfxUndoArray()
1503 {
1504 	while ( !aUndoActions.empty() )
1505 	{
1506 		SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
1507 		aUndoActions.Remove( aUndoActions.size() - 1 );
1508 		delete pAction;
1509 	}
1510 }
1511 
1512 
GetId() const1513 sal_uInt16 SfxLinkUndoAction::GetId() const
1514 {
1515       return pAction ? pAction->GetId() : 0;
1516 }
1517