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 20 : TYPEINIT0(SfxRepeatTarget);
39 :
40 :
41 16465 : SfxRepeatTarget::~SfxRepeatTarget()
42 : {
43 16465 : }
44 :
45 :
46 62 : SfxUndoContext::~SfxUndoContext()
47 : {
48 62 : }
49 :
50 :
51 0 : void SfxUndoAction::SetLinkToSfxLinkUndoAction(SfxLinkUndoAction* pSfxLinkUndoAction)
52 : {
53 0 : mpSfxLinkUndoAction = pSfxLinkUndoAction;
54 0 : }
55 :
56 :
57 341877 : SfxUndoAction::~SfxUndoAction()
58 : {
59 341877 : if(mpSfxLinkUndoAction)
60 : {
61 0 : mpSfxLinkUndoAction->LinkedSfxUndoActionDestructed(*this);
62 0 : mpSfxLinkUndoAction = 0;
63 : }
64 341877 : }
65 :
66 :
67 342610 : SfxUndoAction::SfxUndoAction()
68 342610 : : mpSfxLinkUndoAction(0)
69 : {
70 342610 : }
71 :
72 :
73 0 : bool SfxUndoAction::Merge( SfxUndoAction * )
74 : {
75 0 : return false;
76 : }
77 :
78 :
79 760 : OUString SfxUndoAction::GetComment() const
80 : {
81 760 : return OUString();
82 : }
83 :
84 :
85 :
86 6 : sal_uInt16 SfxUndoAction::GetId() const
87 : {
88 6 : return 0;
89 : }
90 :
91 :
92 100 : OUString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
93 : {
94 100 : return GetComment();
95 : }
96 :
97 :
98 0 : void SfxUndoAction::Undo()
99 : {
100 : // These are only conceptually pure virtual
101 : OSL_FAIL( "pure virtual function called: SfxUndoAction::Undo()" );
102 0 : }
103 :
104 :
105 50 : void SfxUndoAction::UndoWithContext( SfxUndoContext& i_context )
106 : {
107 : (void)i_context;
108 50 : Undo();
109 46 : }
110 :
111 :
112 0 : void SfxUndoAction::Redo()
113 : {
114 : // These are only conceptually pure virtual
115 : OSL_FAIL( "pure virtual function called: SfxUndoAction::Redo()" );
116 0 : }
117 :
118 :
119 4 : void SfxUndoAction::RedoWithContext( SfxUndoContext& i_context )
120 : {
121 : (void)i_context;
122 4 : Redo();
123 0 : }
124 :
125 :
126 0 : void SfxUndoAction::Repeat(SfxRepeatTarget&)
127 : {
128 : // These are only conceptually pure virtual
129 : OSL_FAIL( "pure virtual function called: SfxUndoAction::Repeat()" );
130 0 : }
131 :
132 :
133 :
134 4 : bool SfxUndoAction::CanRepeat(SfxRepeatTarget&) const
135 : {
136 4 : 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 41953 : SfxUndoManager_Data( size_t i_nMaxUndoActionCount )
158 41953 : :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount ) )
159 : ,pActUndoArray( NULL )
160 : ,pFatherUndoArray( NULL )
161 : ,mnMarks( 0 )
162 41953 : ,mnEmptyMark(MARK_INVALID)
163 : ,mbUndoEnabled( true )
164 : ,mbDoing( false )
165 125859 : ,mbClearUntilTopLevel( false )
166 : {
167 41953 : pActUndoArray = pUndoArray;
168 41953 : }
169 :
170 41536 : ~SfxUndoManager_Data()
171 41536 : {
172 41536 : delete pUndoArray;
173 41536 : }
174 : };
175 :
176 :
177 : namespace svl { namespace undo { namespace impl
178 : {
179 : class SVL_DLLPRIVATE LockGuard
180 : {
181 : public:
182 560 : LockGuard( SfxUndoManager& i_manager )
183 560 : :m_manager( i_manager )
184 : {
185 560 : m_manager.ImplEnableUndo_Lock( false );
186 560 : }
187 :
188 560 : ~LockGuard()
189 : {
190 560 : m_manager.ImplEnableUndo_Lock( true );
191 560 : }
192 :
193 : private:
194 : SfxUndoManager& m_manager;
195 : };
196 :
197 : typedef void ( SfxUndoListener::*UndoListenerVoidMethod )();
198 : typedef void ( SfxUndoListener::*UndoListenerStringMethod )( const OUString& );
199 :
200 18337560 : struct SVL_DLLPRIVATE NotifyUndoListener : public ::std::unary_function< SfxUndoListener*, void >
201 : {
202 2247476 : NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
203 : :m_notificationMethod( i_notificationMethod )
204 : ,m_altNotificationMethod( NULL )
205 2247476 : ,m_sActionComment()
206 : {
207 2247476 : }
208 :
209 395924 : NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
210 : :m_notificationMethod( NULL )
211 : ,m_altNotificationMethod( i_notificationMethod )
212 395924 : ,m_sActionComment( i_actionComment )
213 : {
214 395924 : }
215 :
216 2601840 : bool is() const
217 : {
218 2601840 : return ( m_notificationMethod != 0 ) || ( m_altNotificationMethod != 0 );
219 : }
220 :
221 67737 : void operator()( SfxUndoListener* i_listener ) const
222 : {
223 : OSL_PRECOND( is(), "NotifyUndoListener: this will crash!" );
224 67737 : if ( m_altNotificationMethod != 0 )
225 : {
226 53991 : ( i_listener->*m_altNotificationMethod )( m_sActionComment );
227 : }
228 : else
229 : {
230 13746 : ( i_listener->*m_notificationMethod )();
231 : }
232 67737 : }
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 7078529 : UndoManagerGuard( SfxUndoManager_Data& i_managerData )
244 : :m_rManagerData( i_managerData )
245 : ,m_aGuard( i_managerData.aMutex )
246 7078529 : ,m_notifiers()
247 : {
248 7078529 : }
249 :
250 : ~UndoManagerGuard();
251 :
252 560 : void clear()
253 : {
254 560 : m_aGuard.clear();
255 560 : }
256 :
257 560 : void reset()
258 : {
259 560 : m_aGuard.reset();
260 560 : }
261 :
262 128 : void cancelNotifications()
263 : {
264 128 : m_notifiers.clear();
265 128 : }
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 233318 : void markForDeletion( SfxUndoAction* i_action )
273 : {
274 : // remember
275 233318 : if ( i_action )
276 233318 : m_aUndoActionsCleanup.push_back( i_action );
277 233318 : }
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 2205940 : void scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
285 : {
286 2205940 : m_notifiers.push_back( NotifyUndoListener( i_notificationMethod ) );
287 2205940 : }
288 :
289 395924 : void scheduleNotification( UndoListenerStringMethod i_notificationMethod, const OUString& i_actionComment )
290 : {
291 395924 : m_notifiers.push_back( NotifyUndoListener( i_notificationMethod, i_actionComment ) );
292 395924 : }
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 14157058 : UndoManagerGuard::~UndoManagerGuard()
302 : {
303 : // copy members
304 7078529 : UndoListeners aListenersCopy( m_rManagerData.aListeners );
305 :
306 : // release mutex
307 7078529 : m_aGuard.clear();
308 :
309 : // delete all actions
310 14390376 : while ( !m_aUndoActionsCleanup.empty() )
311 : {
312 233318 : SfxUndoAction* pAction = m_aUndoActionsCleanup.front();
313 233318 : m_aUndoActionsCleanup.pop_front();
314 : try
315 : {
316 233318 : delete pAction;
317 : }
318 : catch( const Exception& )
319 : {
320 : DBG_UNHANDLED_EXCEPTION();
321 : }
322 : }
323 :
324 : // handle scheduled notification
325 29041107 : for ( ::std::list< NotifyUndoListener >::const_iterator notifier = m_notifiers.begin();
326 19360738 : notifier != m_notifiers.end();
327 : ++notifier
328 : )
329 : {
330 2601840 : if ( notifier->is() )
331 2601840 : ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), *notifier );
332 7078529 : }
333 7078529 : }
334 : } } }
335 :
336 : using namespace ::svl::undo::impl;
337 :
338 :
339 41953 : SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
340 41953 : :m_pData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
341 : {
342 41953 : }
343 :
344 :
345 83072 : SfxUndoManager::~SfxUndoManager()
346 : {
347 41536 : UndoListeners aListenersCopy;
348 : {
349 41536 : UndoManagerGuard aGuard( *m_pData );
350 41536 : aListenersCopy = m_pData->aListeners;
351 : }
352 :
353 : ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(),
354 41536 : NotifyUndoListener( &SfxUndoListener::undoManagerDying ) );
355 41536 : }
356 :
357 :
358 177895 : void SfxUndoManager::EnableUndo( bool i_enable )
359 : {
360 177895 : UndoManagerGuard aGuard( *m_pData );
361 177895 : ImplEnableUndo_Lock( i_enable );
362 :
363 177895 : }
364 :
365 :
366 179015 : void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
367 : {
368 179015 : if ( m_pData->mbUndoEnabled == i_enable )
369 301597 : return;
370 56433 : m_pData->mbUndoEnabled = i_enable;
371 : }
372 :
373 :
374 4043765 : bool SfxUndoManager::IsUndoEnabled() const
375 : {
376 4043765 : UndoManagerGuard aGuard( *m_pData );
377 4043765 : return ImplIsUndoEnabled_Lock();
378 : }
379 :
380 :
381 4696648 : bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
382 : {
383 4696648 : return m_pData->mbUndoEnabled;
384 : }
385 :
386 :
387 11062 : void SfxUndoManager::SetMaxUndoActionCount( size_t nMaxUndoActionCount )
388 : {
389 11062 : 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 11062 : long nNumToDelete = m_pData->pActUndoArray->aUndoActions.size() - nMaxUndoActionCount;
397 22140 : while ( nNumToDelete > 0 )
398 : {
399 16 : size_t nPos = m_pData->pActUndoArray->aUndoActions.size();
400 16 : 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 16 : if ( nNumToDelete > 0 && m_pData->pActUndoArray->nCurUndoAction > 0 )
409 : {
410 16 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
411 16 : aGuard.markForDeletion( pAction );
412 16 : m_pData->pActUndoArray->aUndoActions.Remove(0);
413 16 : --m_pData->pActUndoArray->nCurUndoAction;
414 16 : --nNumToDelete;
415 : }
416 :
417 16 : if ( nPos == m_pData->pActUndoArray->aUndoActions.size() )
418 0 : break; // Cannot delete more entries
419 : }
420 :
421 11062 : m_pData->pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
422 11062 : }
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 2073049 : void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard& i_guard )
433 : {
434 : // clear array
435 4233676 : while ( !m_pData->pActUndoArray->aUndoActions.empty() )
436 : {
437 87578 : size_t deletePos = m_pData->pActUndoArray->aUndoActions.size() - 1;
438 87578 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ deletePos ].pAction;
439 87578 : i_guard.markForDeletion( pAction );
440 87578 : m_pData->pActUndoArray->aUndoActions.Remove( deletePos );
441 : }
442 :
443 2073049 : m_pData->pActUndoArray->nCurUndoAction = 0;
444 :
445 2073049 : m_pData->mnMarks = 0;
446 2073049 : m_pData->mnEmptyMark = MARK_INVALID;
447 2073049 : }
448 :
449 :
450 2067288 : void SfxUndoManager::Clear()
451 : {
452 2067288 : UndoManagerGuard aGuard( *m_pData );
453 :
454 : OSL_ENSURE( !ImplIsInListAction_Lock(), "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
455 2067288 : ImplClearCurrentLevel_NoNotify( aGuard );
456 :
457 : // notify listeners
458 2067288 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
459 2067288 : }
460 :
461 :
462 5633 : void SfxUndoManager::ClearAllLevels()
463 : {
464 5633 : UndoManagerGuard aGuard( *m_pData );
465 5633 : ImplClearCurrentLevel_NoNotify( aGuard );
466 :
467 5633 : if ( ImplIsInListAction_Lock() )
468 : {
469 0 : m_pData->mbClearUntilTopLevel = true;
470 : }
471 : else
472 : {
473 5633 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
474 5633 : }
475 5633 : }
476 :
477 :
478 37270 : void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
479 : {
480 37270 : UndoManagerGuard aGuard( *m_pData );
481 37270 : ImplClearRedo( aGuard, i_currentLevel );
482 37270 : }
483 :
484 :
485 6 : void SfxUndoManager::ClearRedo()
486 : {
487 : OSL_ENSURE( !IsInListAction(), "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
488 6 : ImplClearRedo_NoLock( CurrentLevel );
489 6 : }
490 :
491 :
492 128 : void SfxUndoManager::Reset()
493 : {
494 128 : UndoManagerGuard aGuard( *m_pData );
495 :
496 : // clear all locks
497 256 : while ( !ImplIsUndoEnabled_Lock() )
498 0 : ImplEnableUndo_Lock( true );
499 :
500 : // cancel all list actions
501 280 : while ( IsInListAction() )
502 24 : ImplLeaveListAction( false, aGuard );
503 :
504 : // clear both stacks
505 128 : ImplClearCurrentLevel_NoNotify( aGuard );
506 :
507 : // cancel the notifications scheduled by ImplLeaveListAction,
508 : // as we want to do an own, dedicated notification
509 128 : aGuard.cancelNotifications();
510 :
511 : // schedule notification
512 128 : aGuard.scheduleNotification( &SfxUndoListener::resetAll );
513 128 : }
514 :
515 :
516 16 : void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
517 : {
518 48 : while ( m_pData->pActUndoArray->nCurUndoAction > 0 )
519 : {
520 16 : SfxUndoAction* pUndoAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
521 16 : m_pData->pActUndoArray->aUndoActions.Remove( 0 );
522 16 : i_guard.markForDeletion( pUndoAction );
523 16 : --m_pData->pActUndoArray->nCurUndoAction;
524 : }
525 : // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
526 16 : }
527 :
528 :
529 103435 : void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard, bool const i_currentLevel )
530 : {
531 103435 : SfxUndoArray* pUndoArray = ( i_currentLevel == IUndoManager::CurrentLevel ) ? m_pData->pActUndoArray : m_pData->pUndoArray;
532 :
533 : // clearance
534 206966 : while ( pUndoArray->aUndoActions.size() > pUndoArray->nCurUndoAction )
535 : {
536 96 : size_t deletePos = pUndoArray->aUndoActions.size() - 1;
537 96 : SfxUndoAction* pAction = pUndoArray->aUndoActions[ deletePos ].pAction;
538 96 : pUndoArray->aUndoActions.Remove( deletePos );
539 96 : i_guard.markForDeletion( pAction );
540 : }
541 :
542 : // notification - only if the top level's stack was cleared
543 103435 : if ( i_currentLevel == IUndoManager::TopLevel )
544 37264 : i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
545 103435 : }
546 :
547 :
548 329305 : bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction *pAction, bool bTryMerge, bool bClearRedo, UndoManagerGuard& i_guard )
549 : {
550 329305 : 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 329305 : SfxUndoAction* pMergeWithAction = m_pData->pActUndoArray->nCurUndoAction ?
558 329305 : m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction : NULL;
559 329305 : if ( bTryMerge && pMergeWithAction )
560 : {
561 6 : bool bMerged = pMergeWithAction->Merge( pAction );
562 6 : if ( bMerged )
563 : {
564 6 : i_guard.markForDeletion( pAction );
565 6 : return false;
566 : }
567 : }
568 :
569 : // clear redo stack, if requested
570 329299 : if ( bClearRedo && ( ImplGetRedoActionCount_Lock( CurrentLevel ) > 0 ) )
571 52 : ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
572 :
573 : // respect max number
574 329299 : if( m_pData->pActUndoArray == m_pData->pUndoArray )
575 : {
576 525669 : while(m_pData->pActUndoArray->aUndoActions.size() >= m_pData->pActUndoArray->nMaxUndoActions)
577 : {
578 49979 : i_guard.markForDeletion( m_pData->pActUndoArray->aUndoActions[0].pAction );
579 49979 : m_pData->pActUndoArray->aUndoActions.Remove(0);
580 49979 : if (m_pData->pActUndoArray->nCurUndoAction > 0)
581 : {
582 49979 : --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 49979 : --m_pData->mnEmptyMark;
590 : }
591 : }
592 :
593 : // append new action
594 329299 : m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ );
595 329299 : return true;
596 : }
597 :
598 :
599 167579 : void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, bool bTryMerge )
600 : {
601 167579 : UndoManagerGuard aGuard( *m_pData );
602 :
603 : // add
604 167579 : if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
605 : {
606 : // notify listeners
607 167573 : aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
608 167579 : }
609 167579 : }
610 :
611 :
612 20788 : size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
613 : {
614 20788 : UndoManagerGuard aGuard( *m_pData );
615 20788 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
616 20788 : return pUndoArray->nCurUndoAction;
617 : }
618 :
619 :
620 593 : OUString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
621 : {
622 593 : UndoManagerGuard aGuard( *m_pData );
623 :
624 593 : OUString sComment;
625 593 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
626 : DBG_ASSERT( nNo < pUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" );
627 593 : if( nNo < pUndoArray->nCurUndoAction )
628 593 : sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
629 593 : 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 29954 : SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const
645 : {
646 29954 : UndoManagerGuard aGuard( *m_pData );
647 :
648 : DBG_ASSERT( nNo < m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
649 29954 : if( nNo >= m_pData->pActUndoArray->nCurUndoAction )
650 0 : return NULL;
651 29954 : 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 6136 : bool SfxUndoManager::IsDoing() const
677 : {
678 6136 : UndoManagerGuard aGuard( *m_pData );
679 6136 : return m_pData->mbDoing;
680 : }
681 :
682 :
683 442 : bool SfxUndoManager::Undo()
684 : {
685 442 : return ImplUndo( NULL );
686 : }
687 :
688 :
689 54 : bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
690 : {
691 54 : return ImplUndo( &i_context );
692 : }
693 :
694 :
695 496 : bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
696 : {
697 496 : UndoManagerGuard aGuard( *m_pData );
698 : OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
699 :
700 992 : ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
701 992 : LockGuard aLockGuard( *this );
702 :
703 496 : if ( ImplIsInListAction_Lock() )
704 : {
705 : OSL_ENSURE( false, "SfxUndoManager::Undo: not possible when within a list action!" );
706 0 : return false;
707 : }
708 :
709 496 : if ( m_pData->pActUndoArray->nCurUndoAction == 0 )
710 : {
711 : OSL_ENSURE( false, "SfxUndoManager::Undo: undo stack is empty!" );
712 0 : return false;
713 : }
714 :
715 496 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ].pAction;
716 992 : 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 496 : aGuard.clear();
722 496 : if ( i_contextOrNull != NULL )
723 54 : pAction->UndoWithContext( *i_contextOrNull );
724 : else
725 442 : pAction->Undo();
726 480 : aGuard.reset();
727 : }
728 16 : catch( ... )
729 : {
730 16 : 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 16 : size_t nCurAction = 0;
735 48 : while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
736 : {
737 32 : 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 16 : ImplClearUndo( aGuard );
742 16 : 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 480 : aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
750 :
751 976 : return true;
752 : }
753 :
754 :
755 3893 : size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
756 : {
757 3893 : UndoManagerGuard aGuard( *m_pData );
758 3893 : return ImplGetRedoActionCount_Lock( i_currentLevel );
759 : }
760 :
761 :
762 171466 : size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
763 : {
764 171466 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
765 171466 : return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction;
766 : }
767 :
768 :
769 6 : SfxUndoAction* SfxUndoManager::GetRedoAction( size_t nNo, bool const i_currentLevel ) const
770 : {
771 6 : UndoManagerGuard aGuard( *m_pData );
772 :
773 6 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
774 6 : if ( (pUndoArray->nCurUndoAction + nNo) > pUndoArray->aUndoActions.size() )
775 : {
776 0 : return NULL;
777 : }
778 6 : return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction;
779 : }
780 :
781 :
782 106 : OUString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
783 : {
784 106 : OUString sComment;
785 212 : UndoManagerGuard aGuard( *m_pData );
786 106 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
787 106 : if ( (pUndoArray->nCurUndoAction + nNo) < pUndoArray->aUndoActions.size() )
788 : {
789 106 : sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
790 : }
791 212 : return sComment;
792 : }
793 :
794 :
795 58 : bool SfxUndoManager::Redo()
796 : {
797 58 : return ImplRedo( NULL );
798 : }
799 :
800 :
801 6 : bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
802 : {
803 6 : return ImplRedo( &i_context );
804 : }
805 :
806 :
807 64 : bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
808 : {
809 64 : UndoManagerGuard aGuard( *m_pData );
810 : OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
811 :
812 128 : ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
813 128 : LockGuard aLockGuard( *this );
814 :
815 64 : if ( ImplIsInListAction_Lock() )
816 : {
817 : OSL_ENSURE( false, "SfxUndoManager::Redo: not possible when within a list action!" );
818 0 : return false;
819 : }
820 :
821 64 : 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 64 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction;
828 128 : 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 64 : aGuard.clear();
834 64 : if ( i_contextOrNull != NULL )
835 6 : pAction->RedoWithContext( *i_contextOrNull );
836 : else
837 58 : pAction->Redo();
838 48 : aGuard.reset();
839 : }
840 16 : catch( ... )
841 : {
842 16 : 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 16 : size_t nCurAction = 0;
847 48 : while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
848 : {
849 32 : 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 16 : ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
854 16 : throw;
855 : }
856 16 : ++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 48 : aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
863 :
864 112 : return true;
865 : }
866 :
867 :
868 702 : size_t SfxUndoManager::GetRepeatActionCount() const
869 : {
870 702 : UndoManagerGuard aGuard( *m_pData );
871 702 : return m_pData->pActUndoArray->aUndoActions.size();
872 : }
873 :
874 :
875 104 : OUString SfxUndoManager::GetRepeatActionComment(SfxRepeatTarget &rTarget) const
876 : {
877 104 : UndoManagerGuard aGuard( *m_pData );
878 104 : return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction
879 104 : ->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 328 : bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
900 : {
901 328 : UndoManagerGuard aGuard( *m_pData );
902 328 : if ( !m_pData->pActUndoArray->aUndoActions.empty() )
903 : {
904 328 : size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1;
905 328 : return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
906 : }
907 0 : return false;
908 : }
909 :
910 :
911 840 : void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
912 : {
913 840 : UndoManagerGuard aGuard( *m_pData );
914 840 : m_pData->aListeners.push_back( &i_listener );
915 840 : }
916 :
917 :
918 840 : void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
919 : {
920 840 : UndoManagerGuard aGuard( *m_pData );
921 2520 : for ( UndoListeners::iterator lookup = m_pData->aListeners.begin();
922 1680 : lookup != m_pData->aListeners.end();
923 : ++lookup
924 : )
925 : {
926 840 : if ( (*lookup) == &i_listener )
927 : {
928 840 : m_pData->aListeners.erase( lookup );
929 840 : break;
930 : }
931 840 : }
932 840 : }
933 :
934 : /**
935 : * Inserts a ListUndoAction and sets its UndoArray as current.
936 : */
937 161726 : void SfxUndoManager::EnterListAction( const OUString& rComment,
938 : const OUString &rRepeatComment, sal_uInt16 nId )
939 : {
940 161726 : UndoManagerGuard aGuard( *m_pData );
941 :
942 161726 : if( !ImplIsUndoEnabled_Lock() )
943 0 : return;
944 :
945 161726 : if ( !m_pData->pUndoArray->nMaxUndoActions )
946 0 : return;
947 :
948 161726 : m_pData->pFatherUndoArray = m_pData->pActUndoArray;
949 161726 : SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
950 161726 : OSL_VERIFY( ImplAddUndoAction_NoNotify( pAction, false, false, aGuard ) );
951 : // expected to succeed: all conditions under which it could fail should have been checked already
952 161726 : m_pData->pActUndoArray = pAction;
953 :
954 : // notification
955 161726 : aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
956 : }
957 :
958 :
959 18038 : bool SfxUndoManager::IsInListAction() const
960 : {
961 18038 : UndoManagerGuard aGuard( *m_pData );
962 18038 : return ImplIsInListAction_Lock();
963 : }
964 :
965 :
966 185955 : bool SfxUndoManager::ImplIsInListAction_Lock() const
967 : {
968 185955 : return ( m_pData->pActUndoArray != m_pData->pUndoArray );
969 : }
970 :
971 :
972 54489 : size_t SfxUndoManager::GetListActionDepth() const
973 : {
974 54489 : UndoManagerGuard aGuard( *m_pData );
975 54489 : size_t nDepth(0);
976 :
977 54489 : SfxUndoArray* pLookup( m_pData->pActUndoArray );
978 128632 : while ( pLookup != m_pData->pUndoArray )
979 : {
980 19654 : pLookup = pLookup->pFatherUndoArray;
981 19654 : ++nDepth;
982 : }
983 :
984 54489 : return nDepth;
985 : }
986 :
987 :
988 161668 : size_t SfxUndoManager::LeaveListAction()
989 : {
990 161668 : UndoManagerGuard aGuard( *m_pData );
991 161668 : size_t nCount = ImplLeaveListAction( false, aGuard );
992 :
993 161668 : if ( m_pData->mbClearUntilTopLevel )
994 : {
995 0 : ImplClearCurrentLevel_NoNotify( aGuard );
996 0 : if ( !ImplIsInListAction_Lock() )
997 : {
998 0 : m_pData->mbClearUntilTopLevel = false;
999 0 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
1000 : }
1001 0 : nCount = 0;
1002 : }
1003 :
1004 161668 : return nCount;
1005 : }
1006 :
1007 :
1008 32 : size_t SfxUndoManager::LeaveAndMergeListAction()
1009 : {
1010 32 : UndoManagerGuard aGuard( *m_pData );
1011 32 : return ImplLeaveListAction( true, aGuard );
1012 : }
1013 :
1014 :
1015 161724 : size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
1016 : {
1017 161724 : if ( !ImplIsUndoEnabled_Lock() )
1018 0 : return 0;
1019 :
1020 161724 : if ( !m_pData->pUndoArray->nMaxUndoActions )
1021 0 : return 0;
1022 :
1023 161724 : if( !ImplIsInListAction_Lock() )
1024 : {
1025 : OSL_TRACE( "svl::SfxUndoManager::ImplLeaveListAction, called without calling EnterListAction()!" );
1026 0 : return 0;
1027 : }
1028 :
1029 : DBG_ASSERT( m_pData->pActUndoArray->pFatherUndoArray, "SfxUndoManager::ImplLeaveListAction, no father undo array!?" );
1030 :
1031 : // the array/level which we're about to leave
1032 161724 : SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray;
1033 : // one step up
1034 161724 : m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray;
1035 :
1036 : // If no undo actions were added to the list, delete the list action
1037 161724 : const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
1038 161724 : if ( nListActionElements == 0 )
1039 : {
1040 95627 : SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1041 95627 : m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
1042 95627 : i_guard.markForDeletion( pCurrentAction );
1043 :
1044 95627 : i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
1045 95627 : return 0;
1046 : }
1047 :
1048 : // now that it is finally clear the list action is non-trivial, and does participate in the Undo stack, clear
1049 : // the redo stack
1050 66097 : ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
1051 :
1052 66097 : SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1053 66097 : SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
1054 66097 : ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
1055 :
1056 66097 : if ( i_merge )
1057 : {
1058 : // merge the list action with its predecessor on the same level
1059 : OSL_ENSURE( m_pData->pActUndoArray->nCurUndoAction > 1,
1060 : "SfxUndoManager::ImplLeaveListAction: cannot merge the list action if there's no other action on the same level - check this beforehand!" );
1061 16 : if ( m_pData->pActUndoArray->nCurUndoAction > 1 )
1062 : {
1063 16 : SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction;
1064 16 : m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 );
1065 16 : --m_pData->pActUndoArray->nCurUndoAction;
1066 16 : pListAction->aUndoActions.Insert( pPreviousAction, 0 );
1067 16 : ++pListAction->nCurUndoAction;
1068 :
1069 16 : pListAction->SetComment( pPreviousAction->GetComment() );
1070 : }
1071 : }
1072 :
1073 : // if the undo array has no comment, try to get it from its children
1074 66097 : if ( pListAction->GetComment().isEmpty() )
1075 : {
1076 82 : for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
1077 : {
1078 82 : if (!pListAction->aUndoActions[n].pAction->GetComment().isEmpty())
1079 : {
1080 42 : pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
1081 42 : break;
1082 : }
1083 : }
1084 : }
1085 :
1086 : // notify listeners
1087 66097 : i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
1088 :
1089 : // outta here
1090 66097 : return nListActionElements;
1091 : }
1092 :
1093 65444 : UndoStackMark SfxUndoManager::MarkTopUndoAction()
1094 : {
1095 65444 : UndoManagerGuard aGuard( *m_pData );
1096 :
1097 : OSL_ENSURE( !IsInListAction(),
1098 : "SfxUndoManager::MarkTopUndoAction(): suspicious call!" );
1099 : OSL_ENSURE((m_pData->mnMarks + 1) < (m_pData->mnEmptyMark - 1),
1100 : "SfxUndoManager::MarkTopUndoAction(): mark overflow!");
1101 :
1102 65444 : size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction;
1103 65444 : if (0 == nActionPos)
1104 : {
1105 65016 : --m_pData->mnEmptyMark;
1106 65016 : return m_pData->mnEmptyMark;
1107 : }
1108 :
1109 428 : m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
1110 856 : ++m_pData->mnMarks );
1111 428 : return m_pData->mnMarks;
1112 : }
1113 :
1114 74 : void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
1115 : {
1116 74 : UndoManagerGuard aGuard( *m_pData );
1117 :
1118 74 : if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
1119 : {
1120 0 : return; // nothing to remove
1121 : }
1122 74 : else if (i_mark == m_pData->mnEmptyMark)
1123 : {
1124 72 : --m_pData->mnEmptyMark; // never returned from MarkTop => invalid
1125 72 : return;
1126 : }
1127 :
1128 134 : for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i )
1129 : {
1130 134 : MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i];
1131 402 : for ( ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
1132 268 : markPos != rAction.aMarks.end();
1133 : ++markPos
1134 : )
1135 : {
1136 2 : if ( *markPos == i_mark )
1137 : {
1138 2 : rAction.aMarks.erase( markPos );
1139 2 : return;
1140 : }
1141 : }
1142 : }
1143 0 : OSL_ENSURE( false, "SfxUndoManager::RemoveMark: mark not found!" );
1144 : // TODO: this might be too offensive. There are situations where we implicitly remove marks
1145 : // without our clients, in particular the client which created the mark, having a chance to know
1146 : // about this.
1147 : }
1148 :
1149 52 : bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
1150 : {
1151 52 : UndoManagerGuard aGuard( *m_pData );
1152 :
1153 52 : size_t nActionPos = m_pData->pUndoArray->nCurUndoAction;
1154 52 : if ( nActionPos == 0 )
1155 : {
1156 16 : return (i_mark == m_pData->mnEmptyMark);
1157 : }
1158 :
1159 : const MarkedUndoAction& rAction =
1160 36 : m_pData->pUndoArray->aUndoActions[ nActionPos-1 ];
1161 108 : for ( ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
1162 72 : markPos != rAction.aMarks.end();
1163 : ++markPos
1164 : )
1165 : {
1166 0 : if ( *markPos == i_mark )
1167 0 : return true;
1168 : }
1169 :
1170 36 : return false;
1171 : }
1172 :
1173 :
1174 0 : void SfxUndoManager::RemoveOldestUndoActions( size_t const i_count )
1175 : {
1176 0 : UndoManagerGuard aGuard( *m_pData );
1177 :
1178 0 : size_t nActionsToRemove = i_count;
1179 0 : while ( nActionsToRemove )
1180 : {
1181 0 : SfxUndoAction* pActionToRemove = m_pData->pUndoArray->aUndoActions[0].pAction;
1182 :
1183 0 : if ( IsInListAction() && ( m_pData->pUndoArray->nCurUndoAction == 1 ) )
1184 : {
1185 : OSL_ENSURE( false, "SfxUndoManager::RemoveOldestUndoActions: cannot remove a not-yet-closed list action!" );
1186 0 : return;
1187 : }
1188 :
1189 0 : aGuard.markForDeletion( pActionToRemove );
1190 0 : m_pData->pUndoArray->aUndoActions.Remove( 0 );
1191 0 : --m_pData->pUndoArray->nCurUndoAction;
1192 0 : --nActionsToRemove;
1193 0 : }
1194 : }
1195 :
1196 :
1197 7606 : sal_uInt16 SfxListUndoAction::GetId() const
1198 : {
1199 7606 : return nId;
1200 : }
1201 :
1202 :
1203 132683 : OUString SfxListUndoAction::GetComment() const
1204 : {
1205 132683 : return aComment;
1206 : }
1207 :
1208 :
1209 11048 : void SfxListUndoAction::SetComment(const OUString& rComment)
1210 : {
1211 11048 : aComment = rComment;
1212 11048 : }
1213 :
1214 :
1215 4 : OUString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
1216 : {
1217 4 : return aRepeatComment;
1218 : }
1219 :
1220 :
1221 :
1222 161726 : SfxListUndoAction::SfxListUndoAction
1223 : (
1224 : const OUString &rComment,
1225 : const OUString &rRepeatComment,
1226 : sal_uInt16 Id,
1227 : SfxUndoArray *pFather
1228 : )
1229 161726 : : nId(Id), aComment(rComment), aRepeatComment(rRepeatComment)
1230 : {
1231 161726 : pFatherUndoArray = pFather;
1232 161726 : nMaxUndoActions = USHRT_MAX;
1233 161726 : }
1234 :
1235 :
1236 62 : void SfxListUndoAction::Undo()
1237 : {
1238 232 : for(size_t i=nCurUndoAction;i>0;)
1239 108 : aUndoActions[--i].pAction->Undo();
1240 62 : nCurUndoAction=0;
1241 62 : }
1242 :
1243 :
1244 34 : void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1245 : {
1246 118 : for(size_t i=nCurUndoAction;i>0;)
1247 50 : aUndoActions[--i].pAction->UndoWithContext( i_context );
1248 34 : nCurUndoAction=0;
1249 34 : }
1250 :
1251 :
1252 4 : void SfxListUndoAction::Redo()
1253 : {
1254 8 : for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1255 4 : aUndoActions[i].pAction->Redo();
1256 4 : nCurUndoAction = aUndoActions.size();
1257 4 : }
1258 :
1259 :
1260 4 : void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
1261 : {
1262 8 : for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1263 4 : aUndoActions[i].pAction->RedoWithContext( i_context );
1264 4 : nCurUndoAction = aUndoActions.size();
1265 4 : }
1266 :
1267 :
1268 0 : void SfxListUndoAction::Repeat(SfxRepeatTarget&rTarget)
1269 : {
1270 0 : for(size_t i=0;i<nCurUndoAction;i++)
1271 0 : aUndoActions[i].pAction->Repeat(rTarget);
1272 0 : }
1273 :
1274 :
1275 4 : bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r) const
1276 : {
1277 8 : for(size_t i=0;i<nCurUndoAction;i++)
1278 : {
1279 4 : if(!aUndoActions[i].pAction->CanRepeat(r))
1280 0 : return false;
1281 : }
1282 4 : return true;
1283 : }
1284 :
1285 :
1286 0 : bool SfxListUndoAction::Merge( SfxUndoAction *pNextAction )
1287 : {
1288 0 : return !aUndoActions.empty() && aUndoActions[aUndoActions.size()-1].pAction->Merge( pNextAction );
1289 : }
1290 :
1291 : /**
1292 : * Creates a LinkAction which points to another UndoManager.
1293 : * Gets that UndoManagers current Action and sets it as that UndoManager's
1294 : * associated Action.
1295 : */
1296 0 : SfxLinkUndoAction::SfxLinkUndoAction(::svl::IUndoManager *pManager)
1297 : {
1298 0 : pUndoManager = pManager;
1299 0 : SfxUndoManager* pUndoManagerImplementation = dynamic_cast< SfxUndoManager* >( pManager );
1300 0 : ENSURE_OR_THROW( pUndoManagerImplementation != NULL, "unsupported undo manager implementation!" );
1301 :
1302 : // yes, this cast is dirty. But reaching into the SfxUndoManager's implementation,
1303 : // directly accessing its internal stack, and tampering with an action on that stack
1304 : // is dirty, too.
1305 0 : if ( pManager->GetMaxUndoActionCount() )
1306 : {
1307 0 : size_t nPos = pManager->GetUndoActionCount()-1;
1308 0 : pAction = pUndoManagerImplementation->m_pData->pActUndoArray->aUndoActions[nPos].pAction;
1309 0 : pAction->SetLinkToSfxLinkUndoAction(this);
1310 : }
1311 : else
1312 0 : pAction = 0;
1313 0 : }
1314 :
1315 :
1316 0 : void SfxLinkUndoAction::Undo()
1317 : {
1318 0 : if ( pAction )
1319 0 : pUndoManager->Undo();
1320 0 : }
1321 :
1322 :
1323 0 : void SfxLinkUndoAction::Redo()
1324 : {
1325 0 : if ( pAction )
1326 0 : pUndoManager->Redo();
1327 0 : }
1328 :
1329 :
1330 :
1331 0 : bool SfxLinkUndoAction::CanRepeat(SfxRepeatTarget& r) const
1332 : {
1333 0 : return pAction && pAction->CanRepeat(r);
1334 : }
1335 :
1336 :
1337 :
1338 :
1339 0 : void SfxLinkUndoAction::Repeat(SfxRepeatTarget&r)
1340 : {
1341 0 : if ( pAction && pAction->CanRepeat( r ) )
1342 0 : pAction->Repeat( r );
1343 0 : }
1344 :
1345 :
1346 :
1347 0 : OUString SfxLinkUndoAction::GetComment() const
1348 : {
1349 0 : if ( pAction )
1350 0 : return pAction->GetComment();
1351 0 : return OUString();
1352 : }
1353 :
1354 :
1355 :
1356 0 : OUString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
1357 : {
1358 0 : if ( pAction )
1359 0 : return pAction->GetRepeatComment(r);
1360 0 : return OUString();
1361 : }
1362 :
1363 :
1364 0 : SfxLinkUndoAction::~SfxLinkUndoAction()
1365 : {
1366 0 : if( pAction )
1367 0 : pAction->SetLinkToSfxLinkUndoAction(0);
1368 0 : }
1369 :
1370 :
1371 0 : void SfxLinkUndoAction::LinkedSfxUndoActionDestructed(const SfxUndoAction& rCandidate)
1372 : {
1373 : OSL_ENSURE(0 != pAction, "OOps, we have no linked SfxUndoAction (!)");
1374 : OSL_ENSURE(pAction == &rCandidate, "OOps, the destroyed and linked UndoActions differ (!)");
1375 : (void)rCandidate;
1376 0 : pAction = 0;
1377 0 : }
1378 :
1379 :
1380 447372 : SfxUndoArray::~SfxUndoArray()
1381 : {
1382 501094 : while ( !aUndoActions.empty() )
1383 : {
1384 95258 : SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
1385 95258 : aUndoActions.Remove( aUndoActions.size() - 1 );
1386 95258 : delete pAction;
1387 : }
1388 244454 : }
1389 :
1390 :
1391 0 : sal_uInt16 SfxLinkUndoAction::GetId() const
1392 : {
1393 0 : return pAction ? pAction->GetId() : 0;
1394 1269 : }
1395 :
1396 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|