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