Branch data 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 [ - + ]: 36 : TYPEINIT0(SfxRepeatTarget);
45 : :
46 : : //------------------------------------------------------------------------
47 : :
48 : 5351 : SfxRepeatTarget::~SfxRepeatTarget()
49 : : {
50 [ - + ]: 5351 : }
51 : :
52 : : //------------------------------------------------------------------------
53 : :
54 : 52 : SfxUndoContext::~SfxUndoContext()
55 : : {
56 [ - + ]: 52 : }
57 : :
58 : : //------------------------------------------------------------------------
59 : :
60 : 12551 : sal_Bool SfxUndoAction::IsLinked()
61 : : {
62 : 12551 : return bLinked;
63 : : }
64 : :
65 : : //------------------------------------------------------------------------
66 : :
67 : 784063 : void SfxUndoAction::SetLinked( sal_Bool bIsLinked )
68 : : {
69 : 784063 : bLinked = bIsLinked;
70 : 784063 : }
71 : :
72 : : //------------------------------------------------------------------------
73 : :
74 : 783874 : SfxUndoAction::~SfxUndoAction()
75 : : {
76 : : DBG_DTOR(SfxUndoAction, 0);
77 : : DBG_ASSERT( !IsLinked(), "Gelinkte Action geloescht" );
78 [ - + ]: 783874 : }
79 : :
80 : :
81 : 784063 : SfxUndoAction::SfxUndoAction()
82 : : {
83 : : DBG_CTOR(SfxUndoAction, 0);
84 : 784063 : SetLinked( sal_False );
85 : 784063 : }
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 : 186 : rtl::OUString SfxUndoAction::GetComment() const
98 : : {
99 : : DBG_CHKTHIS(SfxUndoAction, 0);
100 : 186 : return rtl::OUString();
101 : : }
102 : :
103 : : //------------------------------------------------------------------------
104 : :
105 : :
106 : 4 : sal_uInt16 SfxUndoAction::GetId() const
107 : : {
108 : : DBG_CHKTHIS(SfxUndoAction, 0);
109 : 4 : return 0;
110 : : }
111 : :
112 : : //------------------------------------------------------------------------
113 : :
114 : 150 : XubString SfxUndoAction::GetRepeatComment(SfxRepeatTarget&) const
115 : : {
116 : : DBG_CHKTHIS(SfxUndoAction, 0);
117 [ + - ]: 150 : 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 : 50 : void SfxUndoAction::UndoWithContext( SfxUndoContext& i_context )
131 : : {
132 : : (void)i_context;
133 : 50 : Undo();
134 : 46 : }
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 : 4 : void SfxUndoAction::RedoWithContext( SfxUndoContext& i_context )
147 : : {
148 : : (void)i_context;
149 : 4 : 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 : 27894 : SfxUndoManager_Data( size_t i_nMaxUndoActionCount )
188 [ + - ]: 27894 : :pUndoArray( new SfxUndoArray( i_nMaxUndoActionCount ) )
189 : : ,pActUndoArray( NULL )
190 : : ,pFatherUndoArray( NULL )
191 : : ,mnMarks( 0 )
192 : 27894 : ,mnEmptyMark(MARK_INVALID)
193 : : ,mbUndoEnabled( true )
194 : : ,mbDoing( false )
195 [ + - + - ]: 55788 : ,mbClearUntilTopLevel( false )
196 : : {
197 : 27894 : pActUndoArray = pUndoArray;
198 : 27894 : }
199 : :
200 : 27315 : ~SfxUndoManager_Data()
201 : 27315 : {
202 [ + - ][ + - ]: 27315 : delete pUndoArray;
203 : 27315 : }
204 : : };
205 : :
206 : : //========================================================================
207 : :
208 : : namespace svl { namespace undo { namespace impl
209 : : {
210 : : //--------------------------------------------------------------------
211 : : class SVL_DLLPRIVATE LockGuard
212 : : {
213 : : public:
214 : 408 : LockGuard( SfxUndoManager& i_manager )
215 : 408 : :m_manager( i_manager )
216 : : {
217 : 408 : m_manager.ImplEnableUndo_Lock( false );
218 : 408 : }
219 : :
220 : 408 : ~LockGuard()
221 : : {
222 : 408 : m_manager.ImplEnableUndo_Lock( true );
223 : 408 : }
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 : 17607882 : 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 : 1542124 : NotifyUndoListener( UndoListenerVoidMethod i_notificationMethod )
244 : : :m_notificationMethod( i_notificationMethod )
245 : : ,m_altNotificationMethod( NULL )
246 : 1542124 : ,m_sActionComment()
247 : : {
248 : 1542124 : }
249 : :
250 : 988910 : NotifyUndoListener( UndoListenerStringMethod i_notificationMethod, const String& i_actionComment )
251 : : :m_notificationMethod( NULL )
252 : : ,m_altNotificationMethod( i_notificationMethod )
253 : 988910 : ,m_sActionComment( i_actionComment )
254 : : {
255 : 988910 : }
256 : :
257 : 2503695 : bool is() const
258 : : {
259 [ + + ][ + - ]: 2503695 : return ( m_notificationMethod != 0 ) || ( m_altNotificationMethod != 0 );
260 : : }
261 : :
262 : 1419 : void operator()( SfxUndoListener* i_listener ) const
263 : : {
264 : : OSL_PRECOND( is(), "NotifyUndoListener: this will crash!" );
265 [ + + ]: 1419 : if ( m_altNotificationMethod != 0 )
266 : : {
267 [ - + ]: 1207 : ( i_listener->*m_altNotificationMethod )( m_sActionComment );
268 : : }
269 : : else
270 : : {
271 [ - + ]: 212 : ( i_listener->*m_notificationMethod )();
272 : : }
273 : 1419 : }
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 : 3171778 : UndoManagerGuard( SfxUndoManager_Data& i_managerData )
286 : : :m_rManagerData( i_managerData )
287 : : ,m_aGuard( i_managerData.aMutex )
288 [ + - ][ + - ]: 3171778 : ,m_notifiers()
289 : : {
290 : 3171778 : }
291 : :
292 : : ~UndoManagerGuard();
293 : :
294 : 408 : void clear()
295 : : {
296 : 408 : m_aGuard.clear();
297 : 408 : }
298 : :
299 : 408 : void reset()
300 : : {
301 : 408 : m_aGuard.reset();
302 : 408 : }
303 : :
304 : 128 : void cancelNotifications()
305 : : {
306 : 128 : m_notifiers.clear();
307 : 128 : }
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 : 529779 : void markForDeletion( SfxUndoAction* i_action )
315 : : {
316 : : // remember
317 [ + - ]: 529779 : if ( i_action )
318 : 529779 : m_aUndoActionsCleanup.push_back( i_action );
319 : 529779 : }
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 : 1514809 : void scheduleNotification( UndoListenerVoidMethod i_notificationMethod )
327 : : {
328 [ + - ]: 1514809 : m_notifiers.push_back( NotifyUndoListener( i_notificationMethod ) );
329 : 1514809 : }
330 : :
331 : 988910 : void scheduleNotification( UndoListenerStringMethod i_notificationMethod, const String& i_actionComment )
332 : : {
333 [ + - ]: 988910 : m_notifiers.push_back( NotifyUndoListener( i_notificationMethod, i_actionComment ) );
334 : 988910 : }
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 : 3171778 : UndoManagerGuard::~UndoManagerGuard()
344 : : {
345 : : // copy members
346 [ + - ]: 3171778 : UndoListeners aListenersCopy( m_rManagerData.aListeners );
347 : :
348 : : // release mutex
349 [ + - ]: 3171778 : m_aGuard.clear();
350 : :
351 : : // delete all actions
352 [ + + ]: 3701557 : while ( !m_aUndoActionsCleanup.empty() )
353 : : {
354 [ + - ]: 529779 : SfxUndoAction* pAction = m_aUndoActionsCleanup.front();
355 [ + - ]: 529779 : m_aUndoActionsCleanup.pop_front();
356 : : try
357 : : {
358 [ + - ][ + - ]: 529779 : delete pAction;
359 : : }
360 [ # # ]: 0 : catch( const Exception& )
361 : : {
362 : : DBG_UNHANDLED_EXCEPTION();
363 : : }
364 : : }
365 : :
366 : : // handle scheduled notification
367 [ + + ]: 11350946 : for ( ::std::list< NotifyUndoListener >::const_iterator notifier = m_notifiers.begin();
368 : 5675473 : notifier != m_notifiers.end();
369 : : ++notifier
370 : : )
371 : : {
372 [ + - ]: 2503695 : if ( notifier->is() )
373 [ + - ][ + - ]: 2503695 : ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(), *notifier );
[ + - ][ + - ]
374 : 3171778 : }
375 [ # # ]: 3171778 : }
376 : : } } }
377 : :
378 : : using namespace ::svl::undo::impl;
379 : :
380 : : //========================================================================
381 : :
382 : 27894 : SfxUndoManager::SfxUndoManager( size_t nMaxUndoActionCount )
383 [ + - ][ + - ]: 27894 : :m_pData( new SfxUndoManager_Data( nMaxUndoActionCount ) )
384 : : {
385 : 27894 : }
386 : :
387 : : //------------------------------------------------------------------------
388 : :
389 [ + - ]: 27315 : SfxUndoManager::~SfxUndoManager()
390 : : {
391 [ + - ]: 27315 : UndoListeners aListenersCopy;
392 : : {
393 [ + - ]: 27315 : UndoManagerGuard aGuard( *m_pData );
394 [ + - ][ + - ]: 27315 : aListenersCopy = m_pData->aListeners;
395 : : }
396 : :
397 : : ::std::for_each( aListenersCopy.begin(), aListenersCopy.end(),
398 [ + - ][ + - ]: 27315 : NotifyUndoListener( &SfxUndoListener::undoManagerDying ) );
[ + - ][ + - ]
399 [ - + ]: 27808 : }
400 : :
401 : : //------------------------------------------------------------------------
402 : :
403 : 44165 : void SfxUndoManager::EnableUndo( bool i_enable )
404 : : {
405 [ + - ]: 44165 : UndoManagerGuard aGuard( *m_pData );
406 [ + - ][ + - ]: 44165 : ImplEnableUndo_Lock( i_enable );
407 : :
408 : 44165 : }
409 : :
410 : : //------------------------------------------------------------------------
411 : :
412 : 44981 : void SfxUndoManager::ImplEnableUndo_Lock( bool const i_enable )
413 : : {
414 [ + + ]: 44981 : if ( m_pData->mbUndoEnabled == i_enable )
415 : 44981 : return;
416 : 17898 : m_pData->mbUndoEnabled = i_enable;
417 : : }
418 : :
419 : : //------------------------------------------------------------------------
420 : :
421 : 529985 : bool SfxUndoManager::IsUndoEnabled() const
422 : : {
423 [ + - ]: 529985 : UndoManagerGuard aGuard( *m_pData );
424 [ + - ][ + - ]: 529985 : return ImplIsUndoEnabled_Lock();
425 : : }
426 : :
427 : : //------------------------------------------------------------------------
428 : :
429 : 2338832 : bool SfxUndoManager::ImplIsUndoEnabled_Lock() const
430 : : {
431 : 2338832 : return m_pData->mbUndoEnabled;
432 : : }
433 : :
434 : : //------------------------------------------------------------------------
435 : :
436 : 3312 : void SfxUndoManager::SetMaxUndoActionCount( size_t nMaxUndoActionCount )
437 : : {
438 [ + - ]: 3312 : 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 : 3312 : long nNumToDelete = m_pData->pActUndoArray->aUndoActions.size() - nMaxUndoActionCount;
446 [ + + ]: 3320 : while ( nNumToDelete > 0 )
447 : : {
448 : 8 : size_t nPos = m_pData->pActUndoArray->aUndoActions.size();
449 [ - + ]: 8 : 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 [ + - ][ + - ]: 8 : if ( nNumToDelete > 0 && m_pData->pActUndoArray->nCurUndoAction > 0 )
[ + - ]
461 : : {
462 [ + - ]: 8 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
463 [ + - ][ + - ]: 8 : if ( !pAction->IsLinked() )
464 : : {
465 [ + - ]: 8 : aGuard.markForDeletion( pAction );
466 [ + - ]: 8 : m_pData->pActUndoArray->aUndoActions.Remove(0);
467 : 8 : --m_pData->pActUndoArray->nCurUndoAction;
468 : 8 : --nNumToDelete;
469 : : }
470 : : }
471 : :
472 [ - + ]: 8 : if ( nPos == m_pData->pActUndoArray->aUndoActions.size() )
473 : 0 : break; // Cannot delete more entries
474 : : }
475 : :
476 [ + - ]: 3312 : m_pData->pActUndoArray->nMaxUndoActions = nMaxUndoActionCount;
477 : 3312 : }
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 : 1200756 : void SfxUndoManager::ImplClearCurrentLevel_NoNotify( UndoManagerGuard& i_guard )
490 : : {
491 : : // clear array
492 [ + + ]: 1416034 : while ( !m_pData->pActUndoArray->aUndoActions.empty() )
493 : : {
494 : 215278 : size_t deletePos = m_pData->pActUndoArray->aUndoActions.size() - 1;
495 : 215278 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ deletePos ].pAction;
496 : 215278 : i_guard.markForDeletion( pAction );
497 : 215278 : m_pData->pActUndoArray->aUndoActions.Remove( deletePos );
498 : : }
499 : :
500 : 1200756 : m_pData->pActUndoArray->nCurUndoAction = 0;
501 : :
502 : 1200756 : m_pData->mnMarks = 0;
503 : 1200756 : m_pData->mnEmptyMark = MARK_INVALID;
504 : 1200756 : }
505 : :
506 : : //------------------------------------------------------------------------
507 : :
508 : 1198766 : void SfxUndoManager::Clear()
509 : : {
510 [ + - ]: 1198766 : UndoManagerGuard aGuard( *m_pData );
511 : :
512 : : OSL_ENSURE( !ImplIsInListAction_Lock(), "SfxUndoManager::Clear: suspicious call - do you really wish to clear the current level?" );
513 [ + - ]: 1198766 : ImplClearCurrentLevel_NoNotify( aGuard );
514 : :
515 : : // notify listeners
516 [ + - ][ + - ]: 1198766 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
517 : 1198766 : }
518 : :
519 : : //------------------------------------------------------------------------
520 : :
521 : 1862 : void SfxUndoManager::ClearAllLevels()
522 : : {
523 [ + - ]: 1862 : UndoManagerGuard aGuard( *m_pData );
524 [ + - ]: 1862 : ImplClearCurrentLevel_NoNotify( aGuard );
525 : :
526 [ + - ][ - + ]: 1862 : if ( ImplIsInListAction_Lock() )
527 : : {
528 : 0 : m_pData->mbClearUntilTopLevel = true;
529 : : }
530 : : else
531 : : {
532 [ + - ]: 1862 : aGuard.scheduleNotification( &SfxUndoListener::cleared );
533 [ + - ]: 1862 : }
534 : 1862 : }
535 : :
536 : : //------------------------------------------------------------------------
537 : :
538 : 12240 : void SfxUndoManager::ImplClearRedo_NoLock( bool const i_currentLevel )
539 : : {
540 [ + - ]: 12240 : UndoManagerGuard aGuard( *m_pData );
541 [ + - ][ + - ]: 12240 : ImplClearRedo( aGuard, i_currentLevel );
542 : 12240 : }
543 : :
544 : : //------------------------------------------------------------------------
545 : :
546 : 6 : void SfxUndoManager::ClearRedo()
547 : : {
548 : : OSL_ENSURE( !IsInListAction(), "SfxUndoManager::ClearRedo: suspicious call - do you really wish to clear the current level?" );
549 : 6 : ImplClearRedo_NoLock( CurrentLevel );
550 : 6 : }
551 : :
552 : : //------------------------------------------------------------------------
553 : :
554 : 128 : void SfxUndoManager::Reset()
555 : : {
556 [ + - ]: 128 : UndoManagerGuard aGuard( *m_pData );
557 : :
558 : : // clear all locks
559 [ + - ][ - + ]: 128 : while ( !ImplIsUndoEnabled_Lock() )
560 [ # # ]: 0 : ImplEnableUndo_Lock( true );
561 : :
562 : : // cancel all list actions
563 [ + - ][ + + ]: 152 : while ( IsInListAction() )
564 [ + - ]: 24 : ImplLeaveListAction( false, aGuard );
565 : :
566 : : // clear both stacks
567 [ + - ]: 128 : ImplClearCurrentLevel_NoNotify( aGuard );
568 : :
569 : : // cancel the notifications scheduled by ImplLeaveListAction,
570 : : // as we want to do an own, dedicated notification
571 : 128 : aGuard.cancelNotifications();
572 : :
573 : : // schedule notification
574 [ + - ][ + - ]: 128 : aGuard.scheduleNotification( &SfxUndoListener::resetAll );
575 : 128 : }
576 : :
577 : : //------------------------------------------------------------------------
578 : :
579 : 16 : void SfxUndoManager::ImplClearUndo( UndoManagerGuard& i_guard )
580 : : {
581 [ + + ]: 32 : while ( m_pData->pActUndoArray->nCurUndoAction > 0 )
582 : : {
583 : 16 : SfxUndoAction* pUndoAction = m_pData->pActUndoArray->aUndoActions[0].pAction;
584 : 16 : m_pData->pActUndoArray->aUndoActions.Remove( 0 );
585 : 16 : i_guard.markForDeletion( pUndoAction );
586 : 16 : --m_pData->pActUndoArray->nCurUndoAction;
587 : : }
588 : : // TODO: notifications? We don't have clearedUndo, only cleared and clearedRedo at the SfxUndoListener
589 : 16 : }
590 : :
591 : : //------------------------------------------------------------------------
592 : :
593 : 228762 : void SfxUndoManager::ImplClearRedo( UndoManagerGuard& i_guard, bool const i_currentLevel )
594 : : {
595 [ + + ]: 228762 : SfxUndoArray* pUndoArray = ( i_currentLevel == IUndoManager::CurrentLevel ) ? m_pData->pActUndoArray : m_pData->pUndoArray;
596 : :
597 : : // clearance
598 [ + + ]: 228820 : while ( pUndoArray->aUndoActions.size() > pUndoArray->nCurUndoAction )
599 : : {
600 : 58 : size_t deletePos = pUndoArray->aUndoActions.size() - 1;
601 : 58 : SfxUndoAction* pAction = pUndoArray->aUndoActions[ deletePos ].pAction;
602 : 58 : pUndoArray->aUndoActions.Remove( deletePos );
603 : 58 : i_guard.markForDeletion( pAction );
604 : : }
605 : :
606 : : // notification - only if the top level's stack was cleared
607 [ + + ]: 228762 : if ( i_currentLevel == IUndoManager::TopLevel )
608 : 12234 : i_guard.scheduleNotification( &SfxUndoListener::clearedRedo );
609 : 228762 : }
610 : :
611 : : //------------------------------------------------------------------------
612 : :
613 : 772103 : bool SfxUndoManager::ImplAddUndoAction_NoNotify( SfxUndoAction *pAction, bool bTryMerge, bool bClearRedo, UndoManagerGuard& i_guard )
614 : : {
615 [ + + ][ - + ]: 772103 : if ( !ImplIsUndoEnabled_Lock() || ( m_pData->pActUndoArray->nMaxUndoActions == 0 ) )
[ + + ]
616 : : {
617 : 55 : i_guard.markForDeletion( pAction );
618 : 55 : return false;
619 : : }
620 : :
621 : : // merge, if required
622 : 772048 : SfxUndoAction* pMergeWithAction = m_pData->pActUndoArray->nCurUndoAction ?
623 [ + + ]: 772048 : m_pData->pActUndoArray->aUndoActions[m_pData->pActUndoArray->nCurUndoAction-1].pAction : NULL;
624 [ + + ][ + - ]: 772048 : if ( bTryMerge && pMergeWithAction )
625 : : {
626 : 2 : bool bMerged = pMergeWithAction->Merge( pAction );
627 [ + - ]: 2 : if ( bMerged )
628 : : {
629 : 2 : i_guard.markForDeletion( pAction );
630 : 2 : return false;
631 : : }
632 : : }
633 : :
634 : : // clear redo stack, if requested
635 [ + + ][ + + ]: 772046 : if ( bClearRedo && ( ImplGetRedoActionCount_Lock( CurrentLevel ) > 0 ) )
[ + + ]
636 : 18 : ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
637 : :
638 : : // respect max number
639 [ + + ]: 772046 : if( m_pData->pActUndoArray == m_pData->pUndoArray )
640 : : {
641 [ + + + - ]: 1094315 : while( m_pData->pActUndoArray->aUndoActions.size() >=
[ + + ]
642 : 540886 : m_pData->pActUndoArray->nMaxUndoActions &&
643 : 12543 : !m_pData->pActUndoArray->aUndoActions[0].pAction->IsLinked() )
644 : : {
645 : 12543 : i_guard.markForDeletion( m_pData->pActUndoArray->aUndoActions[0].pAction );
646 : 12543 : m_pData->pActUndoArray->aUndoActions.Remove(0);
647 [ + - ]: 12543 : if (m_pData->pActUndoArray->nCurUndoAction > 0)
648 : : {
649 : 12543 : --m_pData->pActUndoArray->nCurUndoAction;
650 : : }
651 : : }
652 : : }
653 : :
654 : : // append new action
655 : 772046 : m_pData->pActUndoArray->aUndoActions.Insert( pAction, m_pData->pActUndoArray->nCurUndoAction++ );
656 : 772103 : return true;
657 : : }
658 : :
659 : : //------------------------------------------------------------------------
660 : :
661 : 253794 : void SfxUndoManager::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerge )
662 : : {
663 [ + - ]: 253794 : UndoManagerGuard aGuard( *m_pData );
664 : :
665 : : // add
666 [ + - ][ + + ]: 253794 : if ( ImplAddUndoAction_NoNotify( pAction, bTryMerge, true, aGuard ) )
667 : : {
668 : : // notify listeners
669 [ + - ][ + - ]: 253737 : aGuard.scheduleNotification( &SfxUndoListener::undoActionAdded, pAction->GetComment() );
[ + - ][ + - ]
670 [ + - ]: 253794 : }
671 : 253794 : }
672 : :
673 : : //------------------------------------------------------------------------
674 : :
675 : 14248 : size_t SfxUndoManager::GetUndoActionCount( bool const i_currentLevel ) const
676 : : {
677 [ + - ]: 14248 : UndoManagerGuard aGuard( *m_pData );
678 [ + + ]: 14248 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
679 [ + - ]: 14248 : return pUndoArray->nCurUndoAction;
680 : : }
681 : :
682 : : //------------------------------------------------------------------------
683 : :
684 : 645 : rtl::OUString SfxUndoManager::GetUndoActionComment( size_t nNo, bool const i_currentLevel ) const
685 : : {
686 [ + - ]: 645 : UndoManagerGuard aGuard( *m_pData );
687 : :
688 : 645 : rtl::OUString sComment;
689 [ + + ]: 645 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
690 : : DBG_ASSERT( nNo < pUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoActionComment: illegal index!" );
691 [ + - ]: 645 : if( nNo < pUndoArray->nCurUndoAction )
692 [ + - ][ + - ]: 645 : sComment = pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction - 1 - nNo ].pAction->GetComment();
693 [ + - ]: 645 : 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 : 16082 : SfxUndoAction* SfxUndoManager::GetUndoAction( size_t nNo ) const
711 : : {
712 [ + - ]: 16082 : UndoManagerGuard aGuard( *m_pData );
713 : :
714 : : DBG_ASSERT( nNo < m_pData->pActUndoArray->nCurUndoAction, "svl::SfxUndoManager::GetUndoAction(), illegal id!" );
715 [ - + ]: 16082 : if( nNo >= m_pData->pActUndoArray->nCurUndoAction )
716 : 0 : return NULL;
717 [ + - ][ + - ]: 16082 : 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 : 914 : bool SfxUndoManager::IsDoing() const
745 : : {
746 [ + - ]: 914 : UndoManagerGuard aGuard( *m_pData );
747 [ + - ]: 914 : return m_pData->mbDoing;
748 : : }
749 : :
750 : : //------------------------------------------------------------------------
751 : :
752 : 338 : sal_Bool SfxUndoManager::Undo()
753 : : {
754 : 338 : return ImplUndo( NULL );
755 : : }
756 : :
757 : : //------------------------------------------------------------------------
758 : :
759 : 46 : sal_Bool SfxUndoManager::UndoWithContext( SfxUndoContext& i_context )
760 : : {
761 : 46 : return ImplUndo( &i_context );
762 : : }
763 : :
764 : : //------------------------------------------------------------------------
765 : :
766 : 384 : sal_Bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
767 : : {
768 [ + - ]: 384 : UndoManagerGuard aGuard( *m_pData );
769 : : OSL_ENSURE( !IsDoing(), "SfxUndoManager::Undo: *nested* Undo/Redo actions? How this?" );
770 : :
771 [ + - ]: 384 : ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
772 [ + - ]: 384 : LockGuard aLockGuard( *this );
773 : :
774 [ + - ][ - + ]: 384 : 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 [ - + ]: 384 : 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 [ + - ]: 384 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ --m_pData->pActUndoArray->nCurUndoAction ].pAction;
787 [ + - ][ + - ]: 384 : 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 [ + - ]: 384 : aGuard.clear();
793 [ + + ]: 384 : if ( i_contextOrNull != NULL )
794 [ + + ]: 46 : pAction->UndoWithContext( *i_contextOrNull );
795 : : else
796 [ + + ]: 338 : pAction->Undo();
797 [ + - ]: 368 : aGuard.reset();
798 : : }
799 : 32 : catch( ... )
800 : : {
801 [ - + ]: 16 : 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 : 16 : size_t nCurAction = 0;
806 [ + - ]: 32 : while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
807 : : {
808 [ - + ][ + + ]: 32 : 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 [ - + ]: 16 : ImplClearUndo( aGuard );
813 : 16 : 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 [ + - ]: 368 : aGuard.scheduleNotification( &SfxUndoListener::actionUndone, sActionComment );
821 : :
822 [ + - ][ + - ]: 384 : return sal_True;
[ + - ][ + - ]
823 : : }
824 : :
825 : : //------------------------------------------------------------------------
826 : :
827 : 4784 : size_t SfxUndoManager::GetRedoActionCount( bool const i_currentLevel ) const
828 : : {
829 [ + - ]: 4784 : UndoManagerGuard aGuard( *m_pData );
830 [ + - ][ + - ]: 4784 : return ImplGetRedoActionCount_Lock( i_currentLevel );
831 : : }
832 : :
833 : : //------------------------------------------------------------------------
834 : :
835 : 258521 : size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) const
836 : : {
837 [ + + ]: 258521 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
838 : 258521 : return pUndoArray->aUndoActions.size() - pUndoArray->nCurUndoAction;
839 : : }
840 : :
841 : : //------------------------------------------------------------------------
842 : :
843 : 110 : rtl::OUString SfxUndoManager::GetRedoActionComment( size_t nNo, bool const i_currentLevel ) const
844 : : {
845 [ + - ]: 110 : UndoManagerGuard aGuard( *m_pData );
846 [ + + ]: 110 : const SfxUndoArray* pUndoArray = i_currentLevel ? m_pData->pActUndoArray : m_pData->pUndoArray;
847 [ + - ][ + - ]: 110 : return pUndoArray->aUndoActions[ pUndoArray->nCurUndoAction + nNo ].pAction->GetComment();
[ + - ]
848 : : }
849 : :
850 : : //------------------------------------------------------------------------
851 : :
852 : 18 : sal_Bool SfxUndoManager::Redo()
853 : : {
854 : 18 : return ImplRedo( NULL );
855 : : }
856 : :
857 : : //------------------------------------------------------------------------
858 : :
859 : 6 : sal_Bool SfxUndoManager::RedoWithContext( SfxUndoContext& i_context )
860 : : {
861 : 6 : return ImplRedo( &i_context );
862 : : }
863 : :
864 : : //------------------------------------------------------------------------
865 : :
866 : 24 : sal_Bool SfxUndoManager::ImplRedo( SfxUndoContext* i_contextOrNull )
867 : : {
868 [ + - ]: 24 : UndoManagerGuard aGuard( *m_pData );
869 : : OSL_ENSURE( !IsDoing(), "SfxUndoManager::Redo: *nested* Undo/Redo actions? How this?" );
870 : :
871 [ + - ]: 24 : ::comphelper::FlagGuard aDoingGuard( m_pData->mbDoing );
872 [ + - ]: 24 : LockGuard aLockGuard( *this );
873 : :
874 [ + - ][ - + ]: 24 : 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 [ - + ]: 24 : 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 [ + - ]: 24 : SfxUndoAction* pAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction++ ].pAction;
887 [ + - ][ + - ]: 24 : 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 [ + - ]: 24 : aGuard.clear();
893 [ + + ]: 24 : if ( i_contextOrNull != NULL )
894 [ + + ]: 6 : pAction->RedoWithContext( *i_contextOrNull );
895 : : else
896 [ + + ]: 18 : pAction->Redo();
897 [ + - ]: 8 : aGuard.reset();
898 : : }
899 : 32 : catch( ... )
900 : : {
901 [ - + ]: 16 : 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 : 16 : size_t nCurAction = 0;
906 [ + - ]: 32 : while ( nCurAction < m_pData->pActUndoArray->aUndoActions.size() )
907 : : {
908 [ - + ][ + + ]: 32 : 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 [ - + ]: 16 : ImplClearRedo( aGuard, IUndoManager::CurrentLevel );
913 : 16 : throw;
914 : : }
915 : 16 : ++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 [ + - ]: 8 : aGuard.scheduleNotification( &SfxUndoListener::actionRedone, sActionComment );
922 : :
923 [ + - ][ + - ]: 24 : return sal_True;
[ + - ][ + - ]
924 : : }
925 : :
926 : : //------------------------------------------------------------------------
927 : :
928 : 1000 : size_t SfxUndoManager::GetRepeatActionCount() const
929 : : {
930 [ + - ]: 1000 : UndoManagerGuard aGuard( *m_pData );
931 [ + - ]: 1000 : return m_pData->pActUndoArray->aUndoActions.size();
932 : : }
933 : :
934 : : //------------------------------------------------------------------------
935 : :
936 : 158 : XubString SfxUndoManager::GetRepeatActionComment( SfxRepeatTarget &rTarget) const
937 : : {
938 [ + - ]: 158 : UndoManagerGuard aGuard( *m_pData );
939 [ + - ]: 158 : return m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->aUndoActions.size() - 1 ].pAction
940 [ + - ][ + - ]: 158 : ->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 : 360 : sal_Bool SfxUndoManager::CanRepeat( SfxRepeatTarget &rTarget ) const
963 : : {
964 [ + - ]: 360 : UndoManagerGuard aGuard( *m_pData );
965 [ + - ]: 360 : if ( !m_pData->pActUndoArray->aUndoActions.empty() )
966 : : {
967 : 360 : size_t nActionNo = m_pData->pActUndoArray->aUndoActions.size() - 1;
968 [ + - ][ + - ]: 360 : return m_pData->pActUndoArray->aUndoActions[nActionNo].pAction->CanRepeat(rTarget);
969 : : }
970 [ + - ]: 360 : return sal_False;
971 : : }
972 : :
973 : : //------------------------------------------------------------------------
974 : :
975 : 57 : void SfxUndoManager::AddUndoListener( SfxUndoListener& i_listener )
976 : : {
977 [ + - ]: 57 : UndoManagerGuard aGuard( *m_pData );
978 [ + - ][ + - ]: 57 : m_pData->aListeners.push_back( &i_listener );
979 : 57 : }
980 : :
981 : : //------------------------------------------------------------------------
982 : :
983 : 57 : void SfxUndoManager::RemoveUndoListener( SfxUndoListener& i_listener )
984 : : {
985 [ + - ]: 57 : UndoManagerGuard aGuard( *m_pData );
986 [ + - ][ + - ]: 114 : for ( UndoListeners::iterator lookup = m_pData->aListeners.begin();
987 : 57 : lookup != m_pData->aListeners.end();
988 : : ++lookup
989 : : )
990 : : {
991 [ + - ]: 57 : if ( (*lookup) == &i_listener )
992 : : {
993 [ + - ]: 57 : m_pData->aListeners.erase( lookup );
994 : 57 : break;
995 : : }
996 [ + - ]: 57 : }
997 : 57 : }
998 : :
999 : : //------------------------------------------------------------------------
1000 : :
1001 : 518309 : void SfxUndoManager::EnterListAction(
1002 : : const XubString& rComment, const XubString &rRepeatComment, sal_uInt16 nId )
1003 : :
1004 : : /* [Beschreibung]
1005 : :
1006 : : Fuegt eine ListUndoAction ein und setzt dessen UndoArray als aktuelles.
1007 : : */
1008 : :
1009 : : {
1010 [ + - ]: 518309 : UndoManagerGuard aGuard( *m_pData );
1011 : :
1012 [ + - ][ - + ]: 518309 : if( !ImplIsUndoEnabled_Lock() )
1013 : : return;
1014 : :
1015 [ - + ]: 518309 : if ( !m_pData->pUndoArray->nMaxUndoActions )
1016 : : return;
1017 : :
1018 : 518309 : m_pData->pFatherUndoArray = m_pData->pActUndoArray;
1019 [ + - ][ + - ]: 518309 : SfxListUndoAction* pAction = new SfxListUndoAction( rComment, rRepeatComment, nId, m_pData->pActUndoArray );
[ + - ][ + - ]
1020 [ + - ]: 518309 : 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 [ + - ]: 518309 : m_pData->pActUndoArray = pAction;
1023 : :
1024 : : // notification
1025 [ + - ][ + - ]: 518309 : aGuard.scheduleNotification( &SfxUndoListener::listActionEntered, rComment );
[ + - ]
1026 : : }
1027 : :
1028 : : //------------------------------------------------------------------------
1029 : :
1030 : 6602 : bool SfxUndoManager::IsInListAction() const
1031 : : {
1032 [ + - ]: 6602 : UndoManagerGuard aGuard( *m_pData );
1033 [ + - ][ + - ]: 6602 : return ImplIsInListAction_Lock();
1034 : : }
1035 : :
1036 : : //------------------------------------------------------------------------
1037 : :
1038 : 527179 : bool SfxUndoManager::ImplIsInListAction_Lock() const
1039 : : {
1040 : 527179 : return ( m_pData->pActUndoArray != m_pData->pUndoArray );
1041 : : }
1042 : :
1043 : : //------------------------------------------------------------------------
1044 : :
1045 : 869 : size_t SfxUndoManager::GetListActionDepth() const
1046 : : {
1047 [ + - ]: 869 : UndoManagerGuard aGuard( *m_pData );
1048 : 869 : size_t nDepth(0);
1049 : :
1050 : 869 : SfxUndoArray* pLookup( m_pData->pActUndoArray );
1051 [ + + ]: 1371 : while ( pLookup != m_pData->pUndoArray )
1052 : : {
1053 : 502 : pLookup = pLookup->pFatherUndoArray;
1054 : 502 : ++nDepth;
1055 : : }
1056 : :
1057 [ + - ]: 869 : return nDepth;
1058 : : }
1059 : :
1060 : : //------------------------------------------------------------------------
1061 : :
1062 : 518251 : size_t SfxUndoManager::LeaveListAction()
1063 : : {
1064 [ + - ]: 518251 : UndoManagerGuard aGuard( *m_pData );
1065 [ + - ]: 518251 : size_t nCount = ImplLeaveListAction( false, aGuard );
1066 : :
1067 [ - + ]: 518251 : 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 [ + - ]: 518251 : return nCount;
1079 : : }
1080 : :
1081 : : //------------------------------------------------------------------------
1082 : :
1083 : 32 : size_t SfxUndoManager::LeaveAndMergeListAction()
1084 : : {
1085 [ + - ]: 32 : UndoManagerGuard aGuard( *m_pData );
1086 [ + - ][ + - ]: 32 : return ImplLeaveListAction( true, aGuard );
1087 : : }
1088 : :
1089 : : //------------------------------------------------------------------------
1090 : :
1091 : 518307 : size_t SfxUndoManager::ImplLeaveListAction( const bool i_merge, UndoManagerGuard& i_guard )
1092 : : {
1093 [ - + ]: 518307 : if ( !ImplIsUndoEnabled_Lock() )
1094 : 0 : return 0;
1095 : :
1096 [ - + ]: 518307 : if ( !m_pData->pUndoArray->nMaxUndoActions )
1097 : 0 : return 0;
1098 : :
1099 [ - + ]: 518307 : 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 : 518307 : SfxUndoArray* pArrayToLeave = m_pData->pActUndoArray;
1109 : : // one step up
1110 : 518307 : m_pData->pActUndoArray = m_pData->pActUndoArray->pFatherUndoArray;
1111 : :
1112 : : // If no undo actions were added to the list, delete the list action
1113 : 518307 : const size_t nListActionElements = pArrayToLeave->nCurUndoAction;
1114 [ + + ]: 518307 : if ( nListActionElements == 0 )
1115 : : {
1116 : 301819 : SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1117 : 301819 : m_pData->pActUndoArray->aUndoActions.Remove( --m_pData->pActUndoArray->nCurUndoAction );
1118 : 301819 : i_guard.markForDeletion( pCurrentAction );
1119 : :
1120 : 301819 : i_guard.scheduleNotification( &SfxUndoListener::listActionCancelled );
1121 : 301819 : 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 : 216488 : ImplClearRedo( i_guard, IUndoManager::CurrentLevel );
1127 : :
1128 : 216488 : SfxUndoAction* pCurrentAction= m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction-1 ].pAction;
1129 [ - + ]: 216488 : SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction * >( pCurrentAction );
1130 [ - + ]: 216488 : ENSURE_OR_RETURN( pListAction, "SfxUndoManager::ImplLeaveListAction: list action expected at this position!", nListActionElements );
1131 : :
1132 [ + + ]: 216488 : 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 [ + - ]: 16 : if ( m_pData->pActUndoArray->nCurUndoAction > 1 )
1138 : : {
1139 : 16 : SfxUndoAction* pPreviousAction = m_pData->pActUndoArray->aUndoActions[ m_pData->pActUndoArray->nCurUndoAction - 2 ].pAction;
1140 : 16 : m_pData->pActUndoArray->aUndoActions.Remove( m_pData->pActUndoArray->nCurUndoAction - 2 );
1141 : 16 : --m_pData->pActUndoArray->nCurUndoAction;
1142 : 16 : pListAction->aUndoActions.Insert( pPreviousAction, 0 );
1143 : 16 : ++pListAction->nCurUndoAction;
1144 : :
1145 [ + - ][ + - ]: 16 : pListAction->SetComment( pPreviousAction->GetComment() );
[ + - ]
1146 : : }
1147 : : }
1148 : :
1149 : : // if the undo array has no comment, try to get it from its children
1150 [ + + ]: 216488 : if ( pListAction->GetComment().isEmpty() )
1151 : : {
1152 [ + - ]: 124 : for( size_t n = 0; n < pListAction->aUndoActions.size(); n++ )
1153 : : {
1154 [ + + ]: 82 : if (!pListAction->aUndoActions[n].pAction->GetComment().isEmpty())
1155 : : {
1156 [ + - ][ + - ]: 42 : pListAction->SetComment( pListAction->aUndoActions[n].pAction->GetComment() );
[ + - ]
1157 : 42 : break;
1158 : : }
1159 : : }
1160 : : }
1161 : :
1162 : : // notify listeners
1163 [ + - ][ + - ]: 216488 : i_guard.scheduleNotification( &SfxUndoListener::listActionLeft, pListAction->GetComment() );
[ + - ]
1164 : :
1165 : : // outta here
1166 : 518307 : return nListActionElements;
1167 : : }
1168 : :
1169 : : //------------------------------------------------------------------------
1170 : 17216 : UndoStackMark SfxUndoManager::MarkTopUndoAction()
1171 : : {
1172 [ + - ]: 17216 : 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 : 17216 : size_t const nActionPos = m_pData->pUndoArray->nCurUndoAction;
1180 [ + + ]: 17216 : if (0 == nActionPos)
1181 : : {
1182 : 16832 : --m_pData->mnEmptyMark;
1183 : 16832 : return m_pData->mnEmptyMark;
1184 : : }
1185 : :
1186 [ + - ]: 384 : m_pData->pUndoArray->aUndoActions[ nActionPos-1 ].aMarks.push_back(
1187 [ + - ]: 768 : ++m_pData->mnMarks );
1188 [ + - ]: 17216 : return m_pData->mnMarks;
1189 : : }
1190 : :
1191 : : //------------------------------------------------------------------------
1192 : 65 : void SfxUndoManager::RemoveMark( UndoStackMark const i_mark )
1193 : : {
1194 [ + - ]: 65 : UndoManagerGuard aGuard( *m_pData );
1195 : :
1196 [ + - ][ - + ]: 65 : if ((m_pData->mnEmptyMark < i_mark) || (MARK_INVALID == i_mark))
[ - + ]
1197 : : {
1198 : : return; // nothing to remove
1199 : : }
1200 [ + + ]: 65 : else if (i_mark == m_pData->mnEmptyMark)
1201 : : {
1202 : 63 : --m_pData->mnEmptyMark; // never returned from MarkTop => invalid
1203 : : return;
1204 : : }
1205 : :
1206 [ + - ]: 134 : for ( size_t i=0; i<m_pData->pUndoArray->aUndoActions.size(); ++i )
1207 : : {
1208 [ + - ]: 134 : MarkedUndoAction& rAction = m_pData->pUndoArray->aUndoActions[i];
1209 [ # # + - ]: 268 : for ( ::std::vector< UndoStackMark >::iterator markPos = rAction.aMarks.begin();
[ + + ]
1210 : 134 : markPos != rAction.aMarks.end();
1211 : : ++markPos
1212 : : )
1213 : : {
1214 [ + - ][ + - ]: 2 : if ( *markPos == i_mark )
1215 : : {
1216 [ + - ]: 2 : rAction.aMarks.erase( markPos );
1217 : : return;
1218 : : }
1219 : : }
1220 : : }
1221 [ + - ][ - + ]: 65 : 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 : 44 : bool SfxUndoManager::HasTopUndoActionMark( UndoStackMark const i_mark )
1229 : : {
1230 [ + - ]: 44 : UndoManagerGuard aGuard( *m_pData );
1231 : :
1232 : 44 : size_t nActionPos = m_pData->pUndoArray->nCurUndoAction;
1233 [ + + ]: 44 : if ( nActionPos == 0 )
1234 : : {
1235 : 12 : return (i_mark == m_pData->mnEmptyMark);
1236 : : }
1237 : :
1238 : : const MarkedUndoAction& rAction =
1239 [ + - ]: 32 : m_pData->pUndoArray->aUndoActions[ nActionPos-1 ];
1240 [ # # + - ]: 64 : for ( ::std::vector< UndoStackMark >::const_iterator markPos = rAction.aMarks.begin();
[ - + ]
1241 : 32 : markPos != rAction.aMarks.end();
1242 : : ++markPos
1243 : : )
1244 : : {
1245 [ # # ][ # # ]: 0 : if ( *markPos == i_mark )
1246 : 0 : return true;
1247 : : }
1248 : :
1249 [ + - ]: 44 : 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 : 602 : sal_uInt16 SfxListUndoAction::GetId() const
1279 : : {
1280 : 602 : return nId;
1281 : : }
1282 : :
1283 : : //------------------------------------------------------------------------
1284 : :
1285 : 433546 : rtl::OUString SfxListUndoAction::GetComment() const
1286 : : {
1287 : 433546 : return aComment;
1288 : : }
1289 : :
1290 : : //------------------------------------------------------------------------
1291 : :
1292 : 6972 : void SfxListUndoAction::SetComment( const UniString& rComment )
1293 : : {
1294 : 6972 : aComment = rComment;
1295 : 6972 : }
1296 : :
1297 : : //------------------------------------------------------------------------
1298 : :
1299 : 8 : XubString SfxListUndoAction::GetRepeatComment(SfxRepeatTarget &) const
1300 : : {
1301 : 8 : return aRepeatComment;
1302 : : }
1303 : :
1304 : :
1305 : : //------------------------------------------------------------------------
1306 : :
1307 : 518309 : SfxListUndoAction::SfxListUndoAction
1308 : : (
1309 : : const XubString &rComment,
1310 : : const XubString rRepeatComment,
1311 : : sal_uInt16 Id,
1312 : : SfxUndoArray *pFather
1313 : : )
1314 [ + - ][ + - ]: 518309 : : nId(Id), aComment(rComment), aRepeatComment(rRepeatComment)
[ + - ]
1315 : : {
1316 : 518309 : pFatherUndoArray = pFather;
1317 : 518309 : nMaxUndoActions = USHRT_MAX;
1318 : 518309 : }
1319 : :
1320 : : //------------------------------------------------------------------------
1321 : :
1322 : 54 : void SfxListUndoAction::Undo()
1323 : : {
1324 [ + + ]: 152 : for(size_t i=nCurUndoAction;i>0;)
1325 : 98 : aUndoActions[--i].pAction->Undo();
1326 : 54 : nCurUndoAction=0;
1327 : 54 : }
1328 : :
1329 : : //------------------------------------------------------------------------
1330 : :
1331 : 32 : void SfxListUndoAction::UndoWithContext( SfxUndoContext& i_context )
1332 : : {
1333 [ + + ]: 80 : for(size_t i=nCurUndoAction;i>0;)
1334 : 48 : aUndoActions[--i].pAction->UndoWithContext( i_context );
1335 : 32 : nCurUndoAction=0;
1336 : 32 : }
1337 : :
1338 : : //------------------------------------------------------------------------
1339 : :
1340 : 0 : void SfxListUndoAction::Redo()
1341 : : {
1342 [ # # ]: 0 : for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1343 : 0 : aUndoActions[i].pAction->Redo();
1344 : 0 : nCurUndoAction = aUndoActions.size();
1345 : 0 : }
1346 : :
1347 : : //------------------------------------------------------------------------
1348 : :
1349 : 4 : void SfxListUndoAction::RedoWithContext( SfxUndoContext& i_context )
1350 : : {
1351 [ + + ]: 8 : for(size_t i=nCurUndoAction;i<aUndoActions.size();i++)
1352 : 4 : aUndoActions[i].pAction->RedoWithContext( i_context );
1353 : 4 : nCurUndoAction = aUndoActions.size();
1354 : 4 : }
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 : 16 : sal_Bool SfxListUndoAction::CanRepeat(SfxRepeatTarget&r) const
1367 : : {
1368 [ + + ]: 40 : for(size_t i=0;i<nCurUndoAction;i++)
1369 [ - + ]: 24 : if(!aUndoActions[i].pAction->CanRepeat(r))
1370 : 0 : return sal_False;
1371 : 16 : 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 : rtl::OUString SfxLinkUndoAction::GetComment() const
1446 : : {
1447 [ # # ]: 0 : if ( pAction )
1448 : 0 : return pAction->GetComment();
1449 : 0 : return rtl::OUString();
1450 : : }
1451 : :
1452 : :
1453 : : //------------------------------------------------------------------------
1454 : :
1455 : 0 : XubString SfxLinkUndoAction::GetRepeatComment(SfxRepeatTarget&r) const
1456 : : {
1457 [ # # ]: 0 : if ( pAction )
1458 : 0 : return pAction->GetRepeatComment(r);
1459 : : else
1460 : 0 : return XubString();
1461 : : }
1462 : :
1463 : : //------------------------------------------------------------------------
1464 : :
1465 : 0 : SfxLinkUndoAction::~SfxLinkUndoAction()
1466 : : {
1467 [ # # ]: 0 : if( pAction )
1468 [ # # ]: 0 : pAction->SetLinked( sal_False );
1469 [ # # ]: 0 : }
1470 : :
1471 : :
1472 : : //------------------------------------------------------------------------
1473 : :
1474 : 545554 : SfxUndoArray::~SfxUndoArray()
1475 : : {
1476 [ + + ]: 787704 : while ( !aUndoActions.empty() )
1477 : : {
1478 [ + - ]: 242150 : SfxUndoAction *pAction = aUndoActions[ aUndoActions.size() - 1 ].pAction;
1479 [ + - ]: 242150 : aUndoActions.Remove( aUndoActions.size() - 1 );
1480 [ + - ][ + - ]: 242150 : delete pAction;
1481 : : }
1482 : 545554 : }
1483 : :
1484 : :
1485 : 0 : sal_uInt16 SfxLinkUndoAction::GetId() const
1486 : : {
1487 [ # # ]: 0 : return pAction ? pAction->GetId() : 0;
1488 [ + - ][ + - ]: 1062 : }
1489 : :
1490 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|