Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <com/sun/star/uno/Exception.hpp>
22 :
23 : #include <comphelper/flagguard.hxx>
24 : #include <tools/debug.hxx>
25 : #include <tools/diagnose_ex.h>
26 :
27 : #include <svl/undo.hxx>
28 :
29 : #include <vector>
30 : #include <list>
31 : #include <limits>
32 :
33 : using ::com::sun::star::uno::Exception;
34 :
35 0 : TYPEINIT0(SfxUndoAction);
36 0 : TYPEINIT0(SfxListUndoAction);
37 0 : TYPEINIT0(SfxLinkUndoAction);
38 0 : TYPEINIT0(SfxRepeatTarget);
39 :
40 :
41 0 : SfxRepeatTarget::~SfxRepeatTarget()
42 : {
43 0 : }
44 :
45 :
46 0 : SfxUndoContext::~SfxUndoContext()
47 : {
48 0 : }
49 :
50 :
51 0 : void SfxUndoAction::SetLinkToSfxLinkUndoAction(SfxLinkUndoAction* pSfxLinkUndoAction)
52 : {
53 0 : mpSfxLinkUndoAction = pSfxLinkUndoAction;
54 0 : }
55 :
56 :
57 0 : SfxUndoAction::~SfxUndoAction()
58 : {
59 0 : if(mpSfxLinkUndoAction)
60 : {
61 0 : mpSfxLinkUndoAction->LinkedSfxUndoActionDestructed(*this);
62 0 : mpSfxLinkUndoAction = 0;
63 : }
64 0 : }
65 :
66 :
67 0 : SfxUndoAction::SfxUndoAction()
68 0 : : mpSfxLinkUndoAction(0)
69 : {
70 0 : }
71 :
72 :
73 0 : bool SfxUndoAction::Merge( SfxUndoAction * )
74 : {
75 0 : return false;
76 : }
77 :
78 :
79 0 : OUString SfxUndoAction::GetComment() const
80 : {
81 0 : return OUString();
82 : }
83 :
84 :
85 :
86 0 : sal_uInt16 SfxUndoAction::GetId() const
87 : {
88 0 : return 0;
89 : }
90 :
91 :
92 0 : OUString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
93 : {
94 0 : return GetComment();
95 : }
96 :
97 :
98 0 : void SfxUndoAction::Undo()
99 : {
100 : // die sind nur konzeptuell pure virtual
101 : OSL_FAIL( "pure virtual function called: SfxUndoAction::Undo()" );
102 0 : }
103 :
104 :
105 0 : void SfxUndoAction::UndoWithContext( SfxUndoContext& i_context )
106 : {
107 : (void)i_context;
108 0 : Undo();
109 0 : }
110 :
111 :
112 0 : void SfxUndoAction::Redo()
113 : {
114 : // die sind nur konzeptuell pure virtual
115 : OSL_FAIL( "pure virtual function called: SfxUndoAction::Redo()" );
116 0 : }
117 :
118 :
119 0 : void SfxUndoAction::RedoWithContext( SfxUndoContext& i_context )
120 : {
121 : (void)i_context;
122 0 : Redo();
123 0 : }
124 :
125 :
126 0 : void SfxUndoAction::Repeat(SfxRepeatTarget&)
127 : {
128 : // die sind nur konzeptuell pure virtual
129 : OSL_FAIL( "pure virtual function called: SfxUndoAction::Repeat()" );
130 0 : }
131 :
132 :
133 :
134 0 : bool SfxUndoAction::CanRepeat(SfxRepeatTarget&) const
135 : {
136 0 : return true;
137 : }
138 :
139 :
140 : typedef ::std::vector< SfxUndoListener* > UndoListeners;
141 :
142 : struct SVL_DLLPRIVATE SfxUndoManager_Data
143 : {
144 : ::osl::Mutex aMutex;
145 : SfxUndoArray* pUndoArray;
146 : SfxUndoArray* pActUndoArray;
147 : SfxUndoArray* pFatherUndoArray;
148 :
149 : sal_Int32 mnMarks;
150 : sal_Int32 mnEmptyMark;
151 : bool mbUndoEnabled;
152 : bool mbDoing;
153 : bool mbClearUntilTopLevel;
154 :
155 : UndoListeners aListeners;
156 :
157 0 : SfxUndoManager_Data( size_t i_nMaxUndoActionCount )
158 0 : :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount ) )
159 : ,pActUndoArray( NULL )
160 : ,pFatherUndoArray( NULL )
161 : ,mnMarks( 0 )
162 0 : ,mnEmptyMark(MARK_INVALID)
163 : ,mbUndoEnabled( true )
164 : ,mbDoing( false )
165 0 : ,mbClearUntilTopLevel( false )
166 : {
167 0 : pActUndoArray = pUndoArray;
168 0 : }
169 :
170 0 : ~SfxUndoManager_Data()
171 0 : {
172 0 : delete pUndoArray;
173 0 : }
174 : };
175 :
176 :
177 : namespace svl { namespace undo { namespace impl
178 : {
179 : class SVL_DLLPRIVATE LockGuard
180 : {
181 : public:
182 0 : LockGuard( SfxUndoManager& i_manager )
183 0 : :m_manager( i_manager )
184 : {
185 0 : m_manager.ImplEnableUndo_Lock( false );
186 0 : }
187 :
188 0 : ~LockGuard()
189 : {
190 0 : m_manager.ImplEnableUndo_Lock( true );
191 0 : }
192 :
193 : private:
194 : SfxUndoManager& m_manager;
195 : };
196 :
197 : typedef void ( SfxUndoListener::*UndoListenerVoidMethod )();
198 : typedef void ( SfxUndoListener::*UndoListenerStringMethod )( const OUString& );
199 :
200 0 : struct SVL_DLLPRIVATE NotifyUndoListener : public ::std::unary_function< SfxUndoListener*, void >
201 : {
202 0 : NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
203 : :m_notificationMethod( i_notificationMethod )
204 : ,m_altNotificationMethod( NULL )
205 0 : ,m_sActionComment()
206 : {
207 0 : }
208 :
209 0 : NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
210 : :m_notificationMethod( NULL )
211 : ,m_altNotificationMethod( i_notificationMethod )
212 0 : ,m_sActionComment( i_actionComment )
213 : {
214 0 : }
215 :
216 0 : bool is() const
217 : {
218 0 : return ( m_notificationMethod != 0 ) || ( m_altNotificationMethod != 0 );
219 : }
220 :
221 0 : void operator()( SfxUndoListener* i_listener ) const
222 : {
223 : OSL_PRECOND( is(), "NotifyUndoListener: this will crash!" );
224 0 : if ( m_altNotificationMethod != 0 )
225 : {
226 0 : ( i_listener->*m_altNotificationMethod )( m_sActionComment );
227 : }
228 : else
229 : {
230 0 : ( i_listener->*m_notificationMethod )();
231 : }
232 0 : }
233 :
234 : private:
235 : UndoListenerVoidMethod m_notificationMethod;
236 : UndoListenerStringMethod m_altNotificationMethod;
237 : OUString m_sActionComment;
238 : };
239 :
240 : class SVL_DLLPRIVATE UndoManagerGuard
241 : {
242 : public:
243 0 : UndoManagerGuard( SfxUndoManager_Data& i_managerData )
244 : :m_rManagerData( i_managerData )
245 : ,m_aGuard( i_managerData.aMutex )
246 0 : ,m_notifiers()
247 : {
248 0 : }
249 :
250 : ~UndoManagerGuard();
251 :
252 0 : void clear()
253 : {
254 0 : m_aGuard.clear();
255 0 : }
256 :
257 0 : void reset()
258 : {
259 0 : m_aGuard.reset();
260 0 : }
261 :
262 0 : void cancelNotifications()
263 : {
264 0 : m_notifiers.clear();
265 0 : }
266 :
267 : /** marks the given Undo action for deletion
268 :
269 : The Undo action will be put into a list, whose members will be deleted from within the destructor of the
270 : UndoManagerGuard. This deletion will happen without the UndoManager's mutex locked.
271 : */
272 0 : void markForDeletion( SfxUndoAction* i_action )
273 : {
274 : // remember
275 0 : if ( i_action )
276 0 : m_aUndoActionsCleanup.push_back( i_action );
277 0 : }
278 :
279 : /** schedules the given SfxUndoListener method to be called for all registered listeners.
280 :
281 : The notification will happen after the Undo manager's mutex has been released, and after all pending
282 : deletions of Undo actions are done.
283 : */
284 0 : void scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
285 : {
286 0 : m_notifiers.push_back( NotifyUndoListener( i_notificationMethod ) );
287 0 : }
288 :
289 0 : void scheduleNotification( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
290 : {
291 0 : m_notifiers.push_back( NotifyUndoListener( i_notificationMethod, i_actionComment ) );
292 0 : }
293 :
294 : private:
295 : SfxUndoManager_Data& m_rManagerData;
296 : ::osl::ResettableMutexGuard m_aGuard;
297 : ::std::list< SfxUndoAction* > m_aUndoActionsCleanup;
298 : ::std::list< NotifyUndoListener > m_notifiers;
299 : };
300 :
301 0 : UndoManagerGuard::~UndoManagerGuard()
302 : {
303 : // copy members
304 0 : UndoListeners aListenersCopy( m_rManagerData.aListeners );
305 :
306 : // release mutex
307 0 : m_aGuard.clear();
308 :
309 : // delete all actions
310 0 : while ( !m_aUndoActionsCleanup.empty() )
311 : {
312 0 : SfxUndoAction* pAction = m_aUndoActionsCleanup.front();
313 0 : m_aUndoActionsCleanup.pop_front();
314 : try
315 : {
316 0 : delete pAction;
317 : }
318 : catch( const Exception& )
319 : {
320 : DBG_UNHANDLED_EXCEPTION();
321 : }
322 : }
323 :
324 : // handle scheduled notification
325 0 : for ( ::std::list< NotifyUndoListener >::const_iterator notifier = m_notifiers.begin();
326 0 : notifier != m_notifiers.end();
327 : ++notifier
328 : )
329 : {
330 0 : if ( notifier->is() )
331 0 : ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), *notifier );
332 0 : }
333 0 : }
334 : } } }
335 :
336 : using namespace ::svl::undo::impl;
337 :
338 :
339 0 : SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
340 0 : :m_pData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
341 : {
342 0 : }
343 :
344 :
345 0 : SfxUndoManager::~SfxUndoManager()
346 : {
347 0 : UndoListeners aListenersCopy;
348 : {
349 0 : UndoManagerGuard aGuard( *m_pData );
350 0 : aListenersCopy = m_pData->aListeners;
351 : }
352 :
353 : ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(),
354 0 : NotifyUndoListener( &SfxUndoListener::undoManagerDying ) );
355 0 : }
356 :
357 :
358 0 : void SfxUndoManager::EnableUndo( bool i_enable )
359 : {
360 0 : UndoManagerGuard aGuard( *m_pData );
361 0 : ImplEnableUndo_Lock( i_enable );
362 :
363 0 : }
364 :
365 :
366 0 : void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
367 : {
368 0 : if ( m_pData->mbUndoEnabled == i_enable )
369 0 : return;
370 0 : m_pData->mbUndoEnabled = i_enable;
371 : }
372 :
373 :
374 0 : bool SfxUndoManager::IsUndoEnabled() const
375 : {
376 0 : UndoManagerGuard aGuard( *m_pData );
377 0 : return ImplIsUndoEnabled_Lock();
378 : }
379 :
380 :
381 0 : bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
382 : {
383 0 : return m_pData->mbUndoEnabled;
384 : }
385 :
386 :
387 0 : void SfxUndoManager::SetMaxUndoActionCount( size_t nMaxUndoActionCount )
388 : {
389 0 : UndoManagerGuard aGuard( *m_pData );
390 :
391 : // Remove entries from the pActUndoArray when we have to reduce
392 : // the number of entries due to a lower nMaxUndoActionCount.
393 : // Both redo and undo action entries will be removed until we reached the
394 : // new nMaxUndoActionCount.
395 :
396 0 : long nNumToDelete = m_pData->pActUndoArray->aUndoActions.size() - nMaxUndoActionCount;
397 0 : while ( nNumToDelete > 0 )
398 : {
399 0 : size_t nPos = m_pData->pActUndoArray->aUndoActions.size();
400 0 : if ( nPos > m_pData->pActUndoArray->nCurUndoAction )
401 : {
402 0 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[nPos-1].pAction;
403 0 : aGuard.markForDeletion( pAction );
404 0 : m_pData->pActUndoArray->aUndoActions.Remove( nPos-1 );
405 0 : --nNumToDelete;
406 : }
407 :
408 0 : if ( nNumToDelete > 0 && m_pData->pActUndoArray->nCurUndoAction > 0 )
409 : {
410 0 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
411 0 : aGuard.markForDeletion( pAction );
412 0 : m_pData->pActUndoArray->aUndoActions.Remove(0);
413 0 : --m_pData->pActUndoArray->nCurUndoAction;
414 0 : --nNumToDelete;
415 : }
416 :
417 0 : if ( nPos == m_pData->pActUndoArray->aUndoActions.size() )
418 0 : break; // Cannot delete more entries
419 : }
420 :
421 0 : m_pData->pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
422 0 : }
423 :
424 :
425 0 : size_t SfxUndoManager::GetMaxUndoActionCount() const
426 : {
427 0 : UndoManagerGuard aGuard( *m_pData );
428 0 : return m_pData->pActUndoArray->nMaxUndoActions;
429 : }
430 :
431 :
432 0 : void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard& i_guard )
433 : {
434 : // clear array
435 0 : while ( !m_pData->pActUndoArray->aUndoActions.empty() )
436 : {
437 0 : size_t deletePos = m_pData->pActUndoArray->aUndoActions.size() - 1;
438 0 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ deletePos ].pAction;
439 0 : i_guard.markForDeletion( pAction );
440 0 : m_pData->pActUndoArray->aUndoActions.Remove( deletePos );
441 : }
442 :
443 0 : m_pData->pActUndoArray->nCurUndoAction = 0;
444 :
445 0 : m_pData->mnMarks = 0;
446 0 : m_pData->mnEmptyMark = MARK_INVALID;
447 0 : }
448 :
449 :
450 0 : void SfxUndoManager::Clear()
451 : {
452 0 : UndoManagerGuard aGuard( *m_pData );
453 :
454 : OSL_ENSURE( !ImplIsInListAction_Lock(), "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
455 0 : ImplClearCurrentLevel_NoNotify( aGuard );
456 :
457 : // notify listeners
458 0 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
459 0 : }
460 :
461 :
462 0 : void SfxUndoManager::ClearAllLevels()
463 : {
464 0 : UndoManagerGuard aGuard( *m_pData );
465 0 : ImplClearCurrentLevel_NoNotify( aGuard );
466 :
467 0 : if ( ImplIsInListAction_Lock() )
468 : {
469 0 : m_pData->mbClearUntilTopLevel = true;
470 : }
471 : else
472 : {
473 0 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
474 0 : }
475 0 : }
476 :
477 :
478 0 : void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
479 : {
480 0 : UndoManagerGuard aGuard( *m_pData );
481 0 : ImplClearRedo( aGuard, i_currentLevel );
482 0 : }
483 :
484 :
485 0 : void SfxUndoManager::ClearRedo()
486 : {
487 : OSL_ENSURE( !IsInListAction(), "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
488 0 : ImplClearRedo_NoLock( CurrentLevel );
489 0 : }
490 :
491 :
492 0 : void SfxUndoManager::Reset()
493 : {
494 0 : UndoManagerGuard aGuard( *m_pData );
495 :
496 : // clear all locks
497 0 : while ( !ImplIsUndoEnabled_Lock() )
498 0 : ImplEnableUndo_Lock( true );
499 :
500 : // cancel all list actions
501 0 : while ( IsInListAction() )
502 0 : ImplLeaveListAction( false, aGuard );
503 :
504 : // clear both stacks
505 0 : ImplClearCurrentLevel_NoNotify( aGuard );
506 :
507 : // cancel the notifications scheduled by ImplLeaveListAction,
508 : // as we want to do an own, dedicated notification
509 0 : aGuard.cancelNotifications();
510 :
511 : // schedule notification
512 0 : aGuard.scheduleNotification( &SfxUndoListener::resetAll );
513 0 : }
514 :
515 :
516 0 : void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
517 : {
518 0 : while ( m_pData->pActUndoArray->nCurUndoAction > 0 )
519 : {
520 0 : SfxUndoAction* pUndoAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
521 0 : m_pData->pActUndoArray->aUndoActions.Remove( 0 );
522 0 : i_guard.markForDeletion( pUndoAction );
523 0 : --m_pData->pActUndoArray->nCurUndoAction;
524 : }
525 : // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
526 0 : }
527 :
528 :
529 0 : void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard, bool const i_currentLevel )
530 : {
531 0 : SfxUndoArray* pUndoArray = ( i_currentLevel == IUndoManager::CurrentLevel ) ? m_pData->pActUndoArray : m_pData->pUndoArray;
532 :
533 : // clearance
534 0 : while ( pUndoArray->aUndoActions.size() > pUndoArray->nCurUndoAction )
535 : {
536 0 : size_t deletePos = pUndoArray->aUndoActions.size() - 1;
537 0 : SfxUndoAction* pAction = pUndoArray->aUndoActions[ deletePos ].pAction;
538 0 : pUndoArray->aUndoActions.Remove( deletePos );
539 0 : i_guard.markForDeletion( pAction );
540 : }
541 :
542 : // notification - only if the top level's stack was cleared
543 0 : if ( i_currentLevel == IUndoManager::TopLevel )
544 0 : i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
545 0 : }
546 :
547 :
548 0 : bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction *pAction, bool bTryMerge, bool bClearRedo, UndoManagerGuard& i_guard )
549 : {
550 0 : if ( !ImplIsUndoEnabled_Lock() || ( m_pData->pActUndoArray->nMaxUndoActions == 0 ) )
551 : {
552 0 : i_guard.markForDeletion( pAction );
553 0 : return false;
554 : }
555 :
556 : // merge, if required
557 0 : SfxUndoAction* pMergeWithAction = m_pData->pActUndoArray->nCurUndoAction ?
558 0 : m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction : NULL;
559 0 : if ( bTryMerge && pMergeWithAction )
560 : {
561 0 : bool bMerged = pMergeWithAction->Merge( pAction );
562 0 : if ( bMerged )
563 : {
564 0 : i_guard.markForDeletion( pAction );
565 0 : return false;
566 : }
567 : }
568 :
569 : // clear redo stack, if requested
570 0 : if ( bClearRedo && ( ImplGetRedoActionCount_Lock( CurrentLevel ) > 0 ) )
571 0 : ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
572 :
573 : // respect max number
574 0 : if( m_pData->pActUndoArray == m_pData->pUndoArray )
575 : {
576 0 : while(m_pData->pActUndoArray->aUndoActions.size() >= m_pData->pActUndoArray->nMaxUndoActions)
577 : {
578 0 : i_guard.markForDeletion( m_pData->pActUndoArray->aUndoActions[0].pAction );
579 0 : m_pData->pActUndoArray->aUndoActions.Remove(0);
580 0 : if (m_pData->pActUndoArray->nCurUndoAction > 0)
581 : {
582 0 : --m_pData->pActUndoArray->nCurUndoAction;
583 : }
584 : else
585 : {
586 : OSL_ENSURE(false, "CurrentUndoAction going negative (!)");
587 : }
588 : // fdo#66071 invalidate the current empty mark when removing
589 0 : --m_pData->mnEmptyMark;
590 : }
591 : }
592 :
593 : // append new action
594 0 : m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ );
595 0 : return true;
596 : }
597 :
598 :
599 0 : void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, bool bTryMerge )
600 : {
601 0 : UndoManagerGuard aGuard( *m_pData );
602 :
603 : // add
604 0 : if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
605 : {
606 : // notify listeners
607 0 : aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
608 0 : }
609 0 : }
610 :
611 :
612 0 : size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
613 : {
614 0 : UndoManagerGuard aGuard( *m_pData );
615 0 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
616 0 : return pUndoArray->nCurUndoAction;
617 : }
618 :
619 :
620 0 : OUString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
621 : {
622 0 : UndoManagerGuard aGuard( *m_pData );
623 :
624 0 : OUString sComment;
625 0 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
626 : DBG_ASSERT( nNo < pUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" );
627 0 : if( nNo < pUndoArray->nCurUndoAction )
628 0 : sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
629 0 : return sComment;
630 : }
631 :
632 :
633 0 : sal_uInt16 SfxUndoManager::GetUndoActionId() const
634 : {
635 0 : UndoManagerGuard aGuard( *m_pData );
636 :
637 : DBG_ASSERT( m_pData->pActUndoArray->nCurUndoAction > 0, "svl::SfxUndoManager::GetUndoActionId(), illegal id!" );
638 0 : if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
639 0 : return 0;
640 0 : return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction->GetId();
641 : }
642 :
643 :
644 0 : SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const
645 : {
646 0 : UndoManagerGuard aGuard( *m_pData );
647 :
648 : DBG_ASSERT( nNo < m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
649 0 : if( nNo >= m_pData->pActUndoArray->nCurUndoAction )
650 0 : return NULL;
651 0 : return m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1-nNo].pAction;
652 : }
653 :
654 :
655 : /** clears the redo stack and removes the top undo action */
656 0 : void SfxUndoManager::RemoveLastUndoAction()
657 : {
658 0 : UndoManagerGuard aGuard( *m_pData );
659 :
660 0 : ENSURE_OR_RETURN_VOID( m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::RemoveLastUndoAction(), no action to remove?!" );
661 :
662 0 : m_pData->pActUndoArray->nCurUndoAction--;
663 :
664 : // delete redo-actions and top action
665 0 : for ( size_t nPos = m_pData->pActUndoArray->aUndoActions.size(); nPos > m_pData->pActUndoArray->nCurUndoAction; --nPos )
666 : {
667 0 : aGuard.markForDeletion( m_pData->pActUndoArray->aUndoActions[nPos-1].pAction );
668 : }
669 :
670 0 : m_pData->pActUndoArray->aUndoActions.Remove(
671 0 : m_pData->pActUndoArray->nCurUndoAction,
672 0 : m_pData->pActUndoArray->aUndoActions.size() - m_pData->pActUndoArray->nCurUndoAction );
673 : }
674 :
675 :
676 0 : bool SfxUndoManager::IsDoing() const
677 : {
678 0 : UndoManagerGuard aGuard( *m_pData );
679 0 : return m_pData->mbDoing;
680 : }
681 :
682 :
683 0 : bool SfxUndoManager::Undo()
684 : {
685 0 : return ImplUndo( NULL );
686 : }
687 :
688 :
689 0 : bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
690 : {
691 0 : return ImplUndo( &i_context );
692 : }
693 :
694 :
695 0 : bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
696 : {
697 0 : UndoManagerGuard aGuard( *m_pData );
698 : OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
699 :
700 0 : ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
701 0 : LockGuard aLockGuard( *this );
702 :
703 0 : if ( ImplIsInListAction_Lock() )
704 : {
705 : OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" );
706 0 : return false;
707 : }
708 :
709 0 : if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
710 : {
711 : OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" );
712 0 : return false;
713 : }
714 :
715 0 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ].pAction;
716 0 : const OUString sActionComment = pAction->GetComment();
717 : try
718 : {
719 : // clear the guard/mutex before calling into the SfxUndoAction - this can be an extension-implemented UNO component
720 : // nowadays ...
721 0 : aGuard.clear();
722 0 : if ( i_contextOrNull != NULL )
723 0 : pAction->UndoWithContext( *i_contextOrNull );
724 : else
725 0 : pAction->Undo();
726 0 : aGuard.reset();
727 : }
728 0 : catch( ... )
729 : {
730 0 : aGuard.reset();
731 :
732 : // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
733 : // we still find pAction in our current Undo array
734 0 : size_t nCurAction = 0;
735 0 : while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
736 : {
737 0 : if ( m_pData->pActUndoArray->aUndoActions[ nCurAction++ ].pAction == pAction )
738 : {
739 : // the Undo action is still there ...
740 : // assume the error is a permanent failure, and clear the Undo stack
741 0 : ImplClearUndo( aGuard );
742 0 : throw;
743 : }
744 : }
745 : OSL_ENSURE( false, "SfxUndoManager::Undo: can't clear the Undo stack after the failure - some other party was faster ..." );
746 0 : throw;
747 : }
748 :
749 0 : aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
750 :
751 0 : return true;
752 : }
753 :
754 :
755 0 : size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
756 : {
757 0 : UndoManagerGuard aGuard( *m_pData );
758 0 : return ImplGetRedoActionCount_Lock( i_currentLevel );
759 : }
760 :
761 :
762 0 : size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
763 : {
764 0 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
765 0 : return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction;
766 : }
767 :
768 :
769 0 : SfxUndoAction* SfxUndoManager::GetRedoAction( size_t nNo, bool const i_currentLevel ) const
770 : {
771 0 : UndoManagerGuard aGuard( *m_pData );
772 :
773 0 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
774 0 : if ( (pUndoArray->nCurUndoAction + nNo) > pUndoArray->aUndoActions.size() )
775 : {
776 0 : return NULL;
777 : }
778 0 : return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction;
779 : }
780 :
781 :
782 0 : OUString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
783 : {
784 0 : OUString sComment;
785 0 : UndoManagerGuard aGuard( *m_pData );
786 0 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
787 0 : if ( (pUndoArray->nCurUndoAction + nNo) < pUndoArray->aUndoActions.size() )
788 : {
789 0 : sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
790 : }
791 0 : return sComment;
792 : }
793 :
794 :
795 0 : bool SfxUndoManager::Redo()
796 : {
797 0 : return ImplRedo( NULL );
798 : }
799 :
800 :
801 0 : bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
802 : {
803 0 : return ImplRedo( &i_context );
804 : }
805 :
806 :
807 0 : bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
808 : {
809 0 : UndoManagerGuard aGuard( *m_pData );
810 : OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
811 :
812 0 : ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
813 0 : LockGuard aLockGuard( *this );
814 :
815 0 : if ( ImplIsInListAction_Lock() )
816 : {
817 : OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
818 0 : return false;
819 : }
820 :
821 0 : if ( m_pData->pActUndoArray->nCurUndoAction >= m_pData->pActUndoArray->aUndoActions.size() )
822 : {
823 : OSL_ENSURE( false, "SfxUndoManager::Redo: redo stack is empty!" );
824 0 : return false;
825 : }
826 :
827 0 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction;
828 0 : const OUString sActionComment = pAction->GetComment();
829 : try
830 : {
831 : // clear the guard/mutex before calling into the SfxUndoAction - this can be a extension-implemented UNO component
832 : // nowadays ...
833 0 : aGuard.clear();
834 0 : if ( i_contextOrNull != NULL )
835 0 : pAction->RedoWithContext( *i_contextOrNull );
836 : else
837 0 : pAction->Redo();
838 0 : aGuard.reset();
839 : }
840 0 : catch( ... )
841 : {
842 0 : aGuard.reset();
843 :
844 : // in theory, somebody might have tampered with all of *m_pData while the mutex was unlocked. So, see if
845 : // we still find pAction in our current Undo array
846 0 : size_t nCurAction = 0;
847 0 : while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
848 : {
849 0 : if ( m_pData->pActUndoArray->aUndoActions[ nCurAction ].pAction == pAction )
850 : {
851 : // the Undo action is still there ...
852 : // assume the error is a permanent failure, and clear the Undo stack
853 0 : ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
854 0 : throw;
855 : }
856 0 : ++nCurAction;
857 : }
858 : OSL_ENSURE( false, "SfxUndoManager::Redo: can't clear the Undo stack after the failure - some other party was faster ..." );
859 0 : throw;
860 : }
861 :
862 0 : aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
863 :
864 0 : return true;
865 : }
866 :
867 :
868 0 : size_t SfxUndoManager::GetRepeatActionCount() const
869 : {
870 0 : UndoManagerGuard aGuard( *m_pData );
871 0 : return m_pData->pActUndoArray->aUndoActions.size();
872 : }
873 :
874 :
875 0 : OUString SfxUndoManager::GetRepeatActionComment(SfxRepeatTarget &rTarget) const
876 : {
877 0 : UndoManagerGuard aGuard( *m_pData );
878 0 : return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction
879 0 : ->GetRepeatComment(rTarget);
880 : }
881 :
882 :
883 0 : bool SfxUndoManager::Repeat( SfxRepeatTarget &rTarget )
884 : {
885 0 : UndoManagerGuard aGuard( *m_pData );
886 0 : if ( !m_pData->pActUndoArray->aUndoActions.empty() )
887 : {
888 0 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction;
889 0 : aGuard.clear();
890 0 : if ( pAction->CanRepeat( rTarget ) )
891 0 : pAction->Repeat( rTarget );
892 0 : return true;
893 : }
894 :
895 0 : return false;
896 : }
897 :
898 :
899 0 : bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
900 : {
901 0 : UndoManagerGuard aGuard( *m_pData );
902 0 : if ( !m_pData->pActUndoArray->aUndoActions.empty() )
903 : {
904 0 : size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1;
905 0 : return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
906 : }
907 0 : return false;
908 : }
909 :
910 :
911 0 : void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
912 : {
913 0 : UndoManagerGuard aGuard( *m_pData );
914 0 : m_pData->aListeners.push_back( &i_listener );
915 0 : }
916 :
917 :
918 0 : void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
919 : {
920 0 : UndoManagerGuard aGuard( *m_pData );
921 0 : for ( UndoListeners::iterator lookup = m_pData->aListeners.begin();
922 0 : lookup != m_pData->aListeners.end();
923 : ++lookup
924 : )
925 : {
926 0 : if ( (*lookup) == &i_listener )
927 : {
928 0 : m_pData->aListeners.erase( lookup );
929 0 : break;
930 : }
931 0 : }
932 0 : }
933 :
934 :
935 0 : void SfxUndoManager::EnterListAction( const OUString& rComment,
936 : const OUString &rRepeatComment, sal_uInt16 nId )
937 :
938 : /* [Beschreibung]
939 :
940 : Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles.
941 : */
942 :
943 : {
944 0 : UndoManagerGuard aGuard( *m_pData );
945 :
946 0 : if( !ImplIsUndoEnabled_Lock() )
947 0 : return;
948 :
949 0 : if ( !m_pData->pUndoArray->nMaxUndoActions )
950 0 : return;
951 :
952 0 : m_pData->pFatherUndoArray = m_pData->pActUndoArray;
953 0 : SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
954 0 : OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) );
955 : // expected to succeed: all conditions under which it could fail should have been checked already
956 0 : m_pData->pActUndoArray = pAction;
957 :
958 : // notification
959 0 : aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
960 : }
961 :
962 :
963 0 : bool SfxUndoManager::IsInListAction() const
964 : {
965 0 : UndoManagerGuard aGuard( *m_pData );
966 0 : return ImplIsInListAction_Lock();
967 : }
968 :
969 :
970 0 : bool SfxUndoManager::ImplIsInListAction_Lock() const
971 : {
972 0 : return ( m_pData->pActUndoArray != m_pData->pUndoArray );
973 : }
974 :
975 :
976 0 : size_t SfxUndoManager::GetListActionDepth() const
977 : {
978 0 : UndoManagerGuard aGuard( *m_pData );
979 0 : size_t nDepth(0);
980 :
981 0 : SfxUndoArray* pLookup( m_pData->pActUndoArray );
982 0 : while ( pLookup != m_pData->pUndoArray )
983 : {
984 0 : pLookup = pLookup->pFatherUndoArray;
985 0 : ++nDepth;
986 : }
987 :
988 0 : return nDepth;
989 : }
990 :
991 :
992 0 : size_t SfxUndoManager::LeaveListAction()
993 : {
994 0 : UndoManagerGuard aGuard( *m_pData );
995 0 : size_t nCount = ImplLeaveListAction( false, aGuard );
996 :
997 0 : if ( m_pData->mbClearUntilTopLevel )
998 : {
999 0 : ImplClearCurrentLevel_NoNotify( aGuard );
1000 0 : if ( !ImplIsInListAction_Lock() )
1001 : {
1002 0 : m_pData->mbClearUntilTopLevel = false;
1003 0 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
1004 : }
1005 0 : nCount = 0;
1006 : }
1007 :
1008 0 : return nCount;
1009 : }
1010 :
1011 :
1012 0 : size_t SfxUndoManager::LeaveAndMergeListAction()
1013 : {
1014 0 : UndoManagerGuard aGuard( *m_pData );
1015 0 : return ImplLeaveListAction( true, aGuard );
1016 : }
1017 :
1018 :
1019 0 : size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
1020 : {
1021 0 : if ( !ImplIsUndoEnabled_Lock() )
1022 0 : return 0;
1023 :
1024 0 : if ( !m_pData->pUndoArray->nMaxUndoActions )
1025 0 : return 0;
1026 :
1027 0 : if( !ImplIsInListAction_Lock() )
1028 : {
1029 : OSL_TRACE( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
1030 0 : return 0;
1031 : }
1032 :
1033 : DBG_ASSERT( m_pData->pActUndoArray->pFatherUndoArray, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" );
1034 :
1035 : // the array/level which we're about to leave
1036 0 : SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray;
1037 : // one step up
1038 0 : m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray;
1039 :
1040 : // If no undo actions were added to the list, delete the list action
1041 0 : const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
1042 0 : if ( nListActionElements == 0 )
1043 : {
1044 0 : SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1045 0 : m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
1046 0 : i_guard.markForDeletion( pCurrentAction );
1047 :
1048 0 : i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
1049 0 : return 0;
1050 : }
1051 :
1052 : // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1053 : // the redo stack
1054 0 : ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
1055 :
1056 0 : SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1057 0 : SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
1058 0 : ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
1059 :
1060 0 : if ( i_merge )
1061 : {
1062 : // merge the list action with its predecessor on the same level
1063 : OSL_ENSURE( m_pData->pActUndoArray->nCurUndoAction > 1,
1064 : "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
1065 0 : if ( m_pData->pActUndoArray->nCurUndoAction > 1 )
1066 : {
1067 0 : SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction;
1068 0 : m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 );
1069 0 : --m_pData->pActUndoArray->nCurUndoAction;
1070 0 : pListAction->aUndoActions.Insert( pPreviousAction, 0 );
1071 0 : ++pListAction->nCurUndoAction;
1072 :
1073 0 : pListAction->SetComment( pPreviousAction->GetComment() );
1074 : }
1075 : }
1076 :
1077 : // if the undo array has no comment, try to get it from its children
1078 0 : if ( pListAction->GetComment().isEmpty() )
1079 : {
1080 0 : for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
1081 : {
1082 0 : if (!pListAction->aUndoActions[n].pAction->GetComment().isEmpty())
1083 : {
1084 0 : pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
1085 0 : break;
1086 : }
1087 : }
1088 : }
1089 :
1090 : // notify listeners
1091 0 : i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
1092 :
1093 : // outta here
1094 0 : return nListActionElements;
1095 : }
1096 :
1097 0 : UndoStackMark SfxUndoManager::MarkTopUndoAction()
1098 : {
1099 0 : UndoManagerGuard aGuard( *m_pData );
1100 :
1101 : OSL_ENSURE( !IsInListAction(),
1102 : "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
1103 : OSL_ENSURE((m_pData->mnMarks + 1) < (m_pData->mnEmptyMark - 1),
1104 : "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
1105 :
1106 0 : size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction;
1107 0 : if (0 == nActionPos)
1108 : {
1109 0 : --m_pData->mnEmptyMark;
1110 0 : return m_pData->mnEmptyMark;
1111 : }
1112 :
1113 0 : m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
1114 0 : ++m_pData->mnMarks );
1115 0 : return m_pData->mnMarks;
1116 : }
1117 :
1118 0 : void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
1119 : {
1120 0 : UndoManagerGuard aGuard( *m_pData );
1121 :
1122 0 : if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
1123 : {
1124 0 : return; // nothing to remove
1125 : }
1126 0 : else if (i_mark == m_pData->mnEmptyMark)
1127 : {
1128 0 : --m_pData->mnEmptyMark; // never returned from MarkTop => invalid
1129 0 : return;
1130 : }
1131 :
1132 0 : for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i )
1133 : {
1134 0 : MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i];
1135 0 : for ( ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
1136 0 : markPos != rAction.aMarks.end();
1137 : ++markPos
1138 : )
1139 : {
1140 0 : if ( *markPos == i_mark )
1141 : {
1142 0 : rAction.aMarks.erase( markPos );
1143 0 : return;
1144 : }
1145 : }
1146 : }
1147 0 : OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" );
1148 : // TODO: this might be too offensive. There are situations where we implicitly remove marks
1149 : // without our clients, in particular the client which created the mark, having a chance to know
1150 : // about this.
1151 : }
1152 :
1153 0 : bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
1154 : {
1155 0 : UndoManagerGuard aGuard( *m_pData );
1156 :
1157 0 : size_t nActionPos = m_pData->pUndoArray->nCurUndoAction;
1158 0 : if ( nActionPos == 0 )
1159 : {
1160 0 : return (i_mark == m_pData->mnEmptyMark);
1161 : }
1162 :
1163 : const MarkedUndoAction& rAction =
1164 0 : m_pData->pUndoArray->aUndoActions[ nActionPos-1 ];
1165 0 : for ( ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
1166 0 : markPos != rAction.aMarks.end();
1167 : ++markPos
1168 : )
1169 : {
1170 0 : if ( *markPos == i_mark )
1171 0 : return true;
1172 : }
1173 :
1174 0 : return false;
1175 : }
1176 :
1177 :
1178 0 : void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count )
1179 : {
1180 0 : UndoManagerGuard aGuard( *m_pData );
1181 :
1182 0 : size_t nActionsToRemove = i_count;
1183 0 : while ( nActionsToRemove )
1184 : {
1185 0 : SfxUndoAction* pActionToRemove = m_pData->pUndoArray->aUndoActions[0].pAction;
1186 :
1187 0 : if ( IsInListAction() && ( m_pData->pUndoArray->nCurUndoAction == 1 ) )
1188 : {
1189 : OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" );
1190 0 : return;
1191 : }
1192 :
1193 0 : aGuard.markForDeletion( pActionToRemove );
1194 0 : m_pData->pUndoArray->aUndoActions.Remove( 0 );
1195 0 : --m_pData->pUndoArray->nCurUndoAction;
1196 0 : --nActionsToRemove;
1197 0 : }
1198 : }
1199 :
1200 :
1201 0 : sal_uInt16 SfxListUndoAction::GetId() const
1202 : {
1203 0 : return nId;
1204 : }
1205 :
1206 :
1207 0 : OUString SfxListUndoAction::GetComment() const
1208 : {
1209 0 : return aComment;
1210 : }
1211 :
1212 :
1213 0 : void SfxListUndoAction::SetComment(const OUString& rComment)
1214 : {
1215 0 : aComment = rComment;
1216 0 : }
1217 :
1218 :
1219 0 : OUString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
1220 : {
1221 0 : return aRepeatComment;
1222 : }
1223 :
1224 :
1225 :
1226 0 : SfxListUndoAction::SfxListUndoAction
1227 : (
1228 : const OUString &rComment,
1229 : const OUString &rRepeatComment,
1230 : sal_uInt16 Id,
1231 : SfxUndoArray *pFather
1232 : )
1233 0 : : nId(Id), aComment(rComment), aRepeatComment(rRepeatComment)
1234 : {
1235 0 : pFatherUndoArray = pFather;
1236 0 : nMaxUndoActions = USHRT_MAX;
1237 0 : }
1238 :
1239 :
1240 0 : void SfxListUndoAction::Undo()
1241 : {
1242 0 : for(size_t i=nCurUndoAction;i>0;)
1243 0 : aUndoActions[--i].pAction->Undo();
1244 0 : nCurUndoAction=0;
1245 0 : }
1246 :
1247 :
1248 0 : void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1249 : {
1250 0 : for(size_t i=nCurUndoAction;i>0;)
1251 0 : aUndoActions[--i].pAction->UndoWithContext( i_context );
1252 0 : nCurUndoAction=0;
1253 0 : }
1254 :
1255 :
1256 0 : void SfxListUndoAction::Redo()
1257 : {
1258 0 : for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1259 0 : aUndoActions[i].pAction->Redo();
1260 0 : nCurUndoAction = aUndoActions.size();
1261 0 : }
1262 :
1263 :
1264 0 : void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
1265 : {
1266 0 : for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1267 0 : aUndoActions[i].pAction->RedoWithContext( i_context );
1268 0 : nCurUndoAction = aUndoActions.size();
1269 0 : }
1270 :
1271 :
1272 0 : void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
1273 : {
1274 0 : for(size_t i=0;i<nCurUndoAction;i++)
1275 0 : aUndoActions[i].pAction->Repeat(rTarget);
1276 0 : }
1277 :
1278 :
1279 0 : bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r) const
1280 : {
1281 0 : for(size_t i=0;i<nCurUndoAction;i++)
1282 : {
1283 0 : if(!aUndoActions[i].pAction->CanRepeat(r))
1284 0 : return false;
1285 : }
1286 0 : return true;
1287 : }
1288 :
1289 :
1290 0 : bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
1291 : {
1292 0 : return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction );
1293 : }
1294 :
1295 :
1296 0 : SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager)
1297 : /* [Beschreibung]
1298 :
1299 : Richtet eine LinkAction ein, die auf einen weiteren UndoManager zeigt.
1300 : Holt sich als zugehoerige Action des weiteren UndoManagers dessen
1301 : aktuelle Action.
1302 : */
1303 :
1304 : {
1305 0 : pUndoManager = pManager;
1306 0 : SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager );
1307 0 : ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" );
1308 : // yes, this cast is dirty. But reaching into the SfxUndoManager's implementation,
1309 : // directly accessing its internal stack, and tampering with an action on that stack
1310 : // is dirty, too.
1311 0 : if ( pManager->GetMaxUndoActionCount() )
1312 : {
1313 0 : size_t nPos = pManager->GetUndoActionCount()-1;
1314 0 : pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction;
1315 0 : pAction->SetLinkToSfxLinkUndoAction(this);
1316 : }
1317 : else
1318 0 : pAction = 0;
1319 0 : }
1320 :
1321 :
1322 0 : void SfxLinkUndoAction::Undo()
1323 : {
1324 0 : if ( pAction )
1325 0 : pUndoManager->Undo();
1326 0 : }
1327 :
1328 :
1329 0 : void SfxLinkUndoAction::Redo()
1330 : {
1331 0 : if ( pAction )
1332 0 : pUndoManager->Redo();
1333 0 : }
1334 :
1335 :
1336 :
1337 0 : bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
1338 : {
1339 0 : return pAction && pAction->CanRepeat(r);
1340 : }
1341 :
1342 :
1343 :
1344 :
1345 0 : void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r)
1346 : {
1347 0 : if ( pAction && pAction->CanRepeat( r ) )
1348 0 : pAction->Repeat( r );
1349 0 : }
1350 :
1351 :
1352 :
1353 0 : OUString SfxLinkUndoAction::GetComment() const
1354 : {
1355 0 : if ( pAction )
1356 0 : return pAction->GetComment();
1357 0 : return OUString();
1358 : }
1359 :
1360 :
1361 :
1362 0 : OUString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
1363 : {
1364 0 : if ( pAction )
1365 0 : return pAction->GetRepeatComment(r);
1366 0 : return OUString();
1367 : }
1368 :
1369 :
1370 0 : SfxLinkUndoAction::~SfxLinkUndoAction()
1371 : {
1372 0 : if( pAction )
1373 0 : pAction->SetLinkToSfxLinkUndoAction(0);
1374 0 : }
1375 :
1376 :
1377 0 : void SfxLinkUndoAction::LinkedSfxUndoActionDestructed(const SfxUndoAction& rCandidate)
1378 : {
1379 : OSL_ENSURE(0 != pAction, "OOps, we have no linked SfxUndoAction (!)");
1380 : OSL_ENSURE(pAction == &rCandidate, "OOps, the destroyed and linked UndoActions differ (!)");
1381 : (void)rCandidate;
1382 0 : pAction = 0;
1383 0 : }
1384 :
1385 :
1386 0 : SfxUndoArray::~SfxUndoArray()
1387 : {
1388 0 : while ( !aUndoActions.empty() )
1389 : {
1390 0 : SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
1391 0 : aUndoActions.Remove( aUndoActions.size() - 1 );
1392 0 : delete pAction;
1393 : }
1394 0 : }
1395 :
1396 :
1397 0 : sal_uInt16 SfxLinkUndoAction::GetId() const
1398 : {
1399 0 : return pAction ? pAction->GetId() : 0;
1400 3 : }
1401 :
1402 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|