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