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