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