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