Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / framework / source / fwe / helper / undomanagerhelper.cxx
blob080b70eef4c6a6e271e45a2c042006d1063d3b3f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <framework/undomanagerhelper.hxx>
21 #include <framework/imutex.hxx>
23 #include <com/sun/star/document/EmptyUndoStackException.hpp>
24 #include <com/sun/star/document/UndoContextNotClosedException.hpp>
25 #include <com/sun/star/document/UndoFailedException.hpp>
26 #include <com/sun/star/document/XUndoManager.hpp>
27 #include <com/sun/star/lang/XComponent.hpp>
28 #include <com/sun/star/util/InvalidStateException.hpp>
29 #include <com/sun/star/util/NotLockedException.hpp>
30 #include <com/sun/star/util/XModifyListener.hpp>
32 #include <comphelper/interfacecontainer2.hxx>
33 #include <cppuhelper/exc_hlp.hxx>
34 #include <comphelper/flagguard.hxx>
35 #include <comphelper/asyncnotification.hxx>
36 #include <svl/undo.hxx>
37 #include <tools/diagnose_ex.h>
38 #include <osl/conditn.hxx>
40 #include <functional>
41 #include <stack>
42 #include <queue>
44 namespace framework
47 using ::com::sun::star::uno::Reference;
48 using ::com::sun::star::uno::XInterface;
49 using ::com::sun::star::uno::UNO_QUERY;
50 using ::com::sun::star::uno::Exception;
51 using ::com::sun::star::uno::RuntimeException;
52 using ::com::sun::star::uno::Any;
53 using ::com::sun::star::uno::Sequence;
54 using ::com::sun::star::document::XUndoManagerListener;
55 using ::com::sun::star::document::UndoManagerEvent;
56 using ::com::sun::star::document::EmptyUndoStackException;
57 using ::com::sun::star::document::UndoContextNotClosedException;
58 using ::com::sun::star::document::UndoFailedException;
59 using ::com::sun::star::util::NotLockedException;
60 using ::com::sun::star::lang::EventObject;
61 using ::com::sun::star::document::XUndoAction;
62 using ::com::sun::star::lang::XComponent;
63 using ::com::sun::star::document::XUndoManager;
64 using ::com::sun::star::util::InvalidStateException;
65 using ::com::sun::star::lang::IllegalArgumentException;
66 using ::com::sun::star::util::XModifyListener;
68 //= UndoActionWrapper
70 class UndoActionWrapper : public SfxUndoAction
72 public:
73 explicit UndoActionWrapper(
74 Reference< XUndoAction > const& i_undoAction
76 virtual ~UndoActionWrapper() override;
78 virtual OUString GetComment() const override;
79 virtual void Undo() override;
80 virtual void Redo() override;
81 virtual bool CanRepeat(SfxRepeatTarget&) const override;
83 private:
84 const Reference< XUndoAction > m_xUndoAction;
87 UndoActionWrapper::UndoActionWrapper( Reference< XUndoAction > const& i_undoAction )
88 :SfxUndoAction()
89 ,m_xUndoAction( i_undoAction )
91 ENSURE_OR_THROW( m_xUndoAction.is(), "illegal undo action" );
94 UndoActionWrapper::~UndoActionWrapper()
96 try
98 Reference< XComponent > xComponent( m_xUndoAction, UNO_QUERY );
99 if ( xComponent.is() )
100 xComponent->dispose();
102 catch( const Exception& )
104 DBG_UNHANDLED_EXCEPTION("fwk");
108 OUString UndoActionWrapper::GetComment() const
110 OUString sComment;
113 sComment = m_xUndoAction->getTitle();
115 catch( const Exception& )
117 DBG_UNHANDLED_EXCEPTION("fwk");
119 return sComment;
122 void UndoActionWrapper::Undo()
124 m_xUndoAction->undo();
127 void UndoActionWrapper::Redo()
129 m_xUndoAction->redo();
132 bool UndoActionWrapper::CanRepeat(SfxRepeatTarget&) const
134 return false;
137 //= UndoManagerRequest
139 class UndoManagerRequest : public ::comphelper::AnyEvent
141 public:
142 explicit UndoManagerRequest( ::std::function<void ()> const& i_request )
143 :m_request( i_request )
144 ,m_caughtException()
145 ,m_finishCondition()
147 m_finishCondition.reset();
150 void execute()
154 m_request();
156 catch( const Exception& )
158 m_caughtException = ::cppu::getCaughtException();
160 m_finishCondition.set();
163 void wait()
165 m_finishCondition.wait();
166 if ( m_caughtException.hasValue() )
167 ::cppu::throwException( m_caughtException );
170 void cancel( const Reference< XInterface >& i_context )
172 m_caughtException <<= RuntimeException(
173 "Concurrency error: an earlier operation on the stack failed.",
174 i_context
176 m_finishCondition.set();
179 protected:
180 virtual ~UndoManagerRequest() override
184 private:
185 ::std::function<void ()> m_request;
186 Any m_caughtException;
187 ::osl::Condition m_finishCondition;
190 //= UndoManagerHelper_Impl
192 class UndoManagerHelper_Impl : public SfxUndoListener
194 private:
195 ::osl::Mutex m_aMutex;
196 ::osl::Mutex m_aQueueMutex;
197 bool m_bAPIActionRunning;
198 bool m_bProcessingEvents;
199 sal_Int32 m_nLockCount;
200 ::comphelper::OInterfaceContainerHelper2 m_aUndoListeners;
201 ::comphelper::OInterfaceContainerHelper2 m_aModifyListeners;
202 IUndoManagerImplementation& m_rUndoManagerImplementation;
203 ::std::stack< bool > m_aContextVisibilities;
204 #if OSL_DEBUG_LEVEL > 0
205 ::std::stack< bool > m_aContextAPIFlags;
206 #endif
207 ::std::queue< ::rtl::Reference< UndoManagerRequest > >
208 m_aEventQueue;
210 public:
211 ::osl::Mutex& getMutex() { return m_aMutex; }
213 public:
214 explicit UndoManagerHelper_Impl( IUndoManagerImplementation& i_undoManagerImpl )
215 :m_aMutex()
216 ,m_aQueueMutex()
217 ,m_bAPIActionRunning( false )
218 ,m_bProcessingEvents( false )
219 ,m_nLockCount( 0 )
220 ,m_aUndoListeners( m_aMutex )
221 ,m_aModifyListeners( m_aMutex )
222 ,m_rUndoManagerImplementation( i_undoManagerImpl )
224 getUndoManager().AddUndoListener( *this );
227 virtual ~UndoManagerHelper_Impl()
231 SfxUndoManager& getUndoManager() const
233 return m_rUndoManagerImplementation.getImplUndoManager();
236 Reference< XUndoManager > getXUndoManager() const
238 return m_rUndoManagerImplementation.getThis();
241 // SfxUndoListener
242 virtual void actionUndone( const OUString& i_actionComment ) override;
243 virtual void actionRedone( const OUString& i_actionComment ) override;
244 virtual void undoActionAdded( const OUString& i_actionComment ) override;
245 virtual void cleared() override;
246 virtual void clearedRedo() override;
247 virtual void resetAll() override;
248 virtual void listActionEntered( const OUString& i_comment ) override;
249 virtual void listActionLeft( const OUString& i_comment ) override;
250 virtual void listActionCancelled() override;
252 // public operations
253 void disposing();
255 void enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock );
256 void leaveUndoContext( IMutexGuard& i_instanceLock );
257 void addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock );
258 void undo( IMutexGuard& i_instanceLock );
259 void redo( IMutexGuard& i_instanceLock );
260 void clear( IMutexGuard& i_instanceLock );
261 void clearRedo( IMutexGuard& i_instanceLock );
262 void reset( IMutexGuard& i_instanceLock );
264 void lock();
265 void unlock();
267 void addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
269 m_aUndoListeners.addInterface( i_listener );
272 void removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
274 m_aUndoListeners.removeInterface( i_listener );
277 void addModifyListener( const Reference< XModifyListener >& i_listener )
279 m_aModifyListeners.addInterface( i_listener );
282 void removeModifyListener( const Reference< XModifyListener >& i_listener )
284 m_aModifyListeners.removeInterface( i_listener );
287 UndoManagerEvent
288 buildEvent( OUString const& i_title ) const;
290 void impl_notifyModified();
291 void notify( OUString const& i_title,
292 void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& )
294 void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) );
296 private:
297 /// adds a function to be called to the request processor's queue
298 void impl_processRequest(::std::function<void ()> const& i_request, IMutexGuard& i_instanceLock);
300 /// impl-versions of the XUndoManager API.
301 void impl_enterUndoContext( const OUString& i_title, const bool i_hidden );
302 void impl_leaveUndoContext();
303 void impl_addUndoAction( const Reference< XUndoAction >& i_action );
304 void impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo );
305 void impl_clear();
306 void impl_clearRedo();
307 void impl_reset();
310 void UndoManagerHelper_Impl::disposing()
312 EventObject aEvent;
313 aEvent.Source = getXUndoManager();
314 m_aUndoListeners.disposeAndClear( aEvent );
315 m_aModifyListeners.disposeAndClear( aEvent );
317 ::osl::MutexGuard aGuard( m_aMutex );
319 getUndoManager().RemoveUndoListener( *this );
322 UndoManagerEvent UndoManagerHelper_Impl::buildEvent( OUString const& i_title ) const
324 UndoManagerEvent aEvent;
325 aEvent.Source = getXUndoManager();
326 aEvent.UndoActionTitle = i_title;
327 aEvent.UndoContextDepth = getUndoManager().GetListActionDepth();
328 return aEvent;
331 void UndoManagerHelper_Impl::impl_notifyModified()
333 const EventObject aEvent( getXUndoManager() );
334 m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent );
337 void UndoManagerHelper_Impl::notify( OUString const& i_title,
338 void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) )
340 const UndoManagerEvent aEvent( buildEvent( i_title ) );
342 // TODO: this notification method here is used by UndoManagerHelper_Impl, to multiplex the notifications we
343 // receive from the SfxUndoManager. Those notifications are sent with a locked SolarMutex, which means
344 // we're doing the multiplexing here with a locked SM, too. Which is Bad (TM).
345 // Fixing this properly would require outsourcing all the notifications into an own thread - which might lead
346 // to problems of its own, since clients might expect synchronous notifications.
348 m_aUndoListeners.notifyEach( i_notificationMethod, aEvent );
349 impl_notifyModified();
352 void UndoManagerHelper_Impl::notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) )
354 const EventObject aEvent( getXUndoManager() );
356 // TODO: the same comment as in the other notify, regarding SM locking applies here ...
358 m_aUndoListeners.notifyEach( i_notificationMethod, aEvent );
359 impl_notifyModified();
362 void UndoManagerHelper_Impl::enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock )
364 impl_processRequest(
365 [this, &i_title, i_hidden] () { return this->impl_enterUndoContext(i_title, i_hidden); },
366 i_instanceLock
370 void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard& i_instanceLock )
372 impl_processRequest(
373 [this] () { return this->impl_leaveUndoContext(); },
374 i_instanceLock
378 void UndoManagerHelper_Impl::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
380 if ( !i_action.is() )
381 throw IllegalArgumentException(
382 "illegal undo action object",
383 getXUndoManager(),
387 impl_processRequest(
388 [this, &i_action] () { return this->impl_addUndoAction(i_action); },
389 i_instanceLock
393 void UndoManagerHelper_Impl::clear( IMutexGuard& i_instanceLock )
395 impl_processRequest(
396 [this] () { return this->impl_clear(); },
397 i_instanceLock
401 void UndoManagerHelper_Impl::clearRedo( IMutexGuard& i_instanceLock )
403 impl_processRequest(
404 [this] () { return this->impl_clearRedo(); },
405 i_instanceLock
409 void UndoManagerHelper_Impl::reset( IMutexGuard& i_instanceLock )
411 impl_processRequest(
412 [this] () { return this->impl_reset(); },
413 i_instanceLock
417 void UndoManagerHelper_Impl::lock()
419 // SYNCHRONIZED --->
420 ::osl::MutexGuard aGuard( getMutex() );
422 if ( ++m_nLockCount == 1 )
424 SfxUndoManager& rUndoManager = getUndoManager();
425 rUndoManager.EnableUndo( false );
427 // <--- SYNCHRONIZED
430 void UndoManagerHelper_Impl::unlock()
432 // SYNCHRONIZED --->
433 ::osl::MutexGuard aGuard( getMutex() );
435 if ( m_nLockCount == 0 )
436 throw NotLockedException( "Undo manager is not locked", getXUndoManager() );
438 if ( --m_nLockCount == 0 )
440 SfxUndoManager& rUndoManager = getUndoManager();
441 rUndoManager.EnableUndo( true );
443 // <--- SYNCHRONIZED
446 void UndoManagerHelper_Impl::impl_processRequest(::std::function<void ()> const& i_request, IMutexGuard& i_instanceLock)
448 // create the request, and add it to our queue
449 ::rtl::Reference< UndoManagerRequest > pRequest( new UndoManagerRequest( i_request ) );
451 ::osl::MutexGuard aQueueGuard( m_aQueueMutex );
452 m_aEventQueue.push( pRequest );
455 i_instanceLock.clear();
457 if ( m_bProcessingEvents )
459 // another thread is processing the event queue currently => it will also process the event which we just added
460 pRequest->wait();
461 return;
464 m_bProcessingEvents = true;
467 pRequest.clear();
469 ::osl::MutexGuard aQueueGuard( m_aQueueMutex );
470 if ( m_aEventQueue.empty() )
472 // reset the flag before releasing the queue mutex, otherwise it's possible that another thread
473 // could add an event after we release the mutex, but before we reset the flag. If then this other
474 // thread checks the flag before be reset it, this thread's event would starve.
475 m_bProcessingEvents = false;
476 return;
478 pRequest = m_aEventQueue.front();
479 m_aEventQueue.pop();
483 pRequest->execute();
484 pRequest->wait();
486 catch( ... )
489 // no chance to process further requests, if the current one failed
490 // => discard them
491 ::osl::MutexGuard aQueueGuard( m_aQueueMutex );
492 while ( !m_aEventQueue.empty() )
494 pRequest = m_aEventQueue.front();
495 m_aEventQueue.pop();
496 pRequest->cancel( getXUndoManager() );
498 m_bProcessingEvents = false;
500 // re-throw the error
501 throw;
504 while ( true );
507 void UndoManagerHelper_Impl::impl_enterUndoContext( const OUString& i_title, const bool i_hidden )
509 // SYNCHRONIZED --->
510 ::osl::ClearableMutexGuard aGuard( m_aMutex );
512 SfxUndoManager& rUndoManager = getUndoManager();
513 if ( !rUndoManager.IsUndoEnabled() )
514 // ignore this request if the manager is locked
515 return;
517 if ( i_hidden && ( rUndoManager.GetUndoActionCount() == 0 ) )
518 throw EmptyUndoStackException(
519 "can't enter a hidden context without a previous Undo action",
520 m_rUndoManagerImplementation.getThis()
524 ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
525 rUndoManager.EnterListAction( i_title, OUString(), 0, ViewShellId(-1) );
528 m_aContextVisibilities.push( i_hidden );
530 const UndoManagerEvent aEvent( buildEvent( i_title ) );
531 aGuard.clear();
532 // <--- SYNCHRONIZED
534 m_aUndoListeners.notifyEach( i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, aEvent );
535 impl_notifyModified();
538 void UndoManagerHelper_Impl::impl_leaveUndoContext()
540 // SYNCHRONIZED --->
541 ::osl::ClearableMutexGuard aGuard( m_aMutex );
543 SfxUndoManager& rUndoManager = getUndoManager();
544 if ( !rUndoManager.IsUndoEnabled() )
545 // ignore this request if the manager is locked
546 return;
548 if ( !rUndoManager.IsInListAction() )
549 throw InvalidStateException(
550 "no active undo context",
551 getXUndoManager()
554 size_t nContextElements = 0;
556 const bool isHiddenContext = m_aContextVisibilities.top();
557 m_aContextVisibilities.pop();
559 const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 );
561 ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
562 if ( isHiddenContext )
563 nContextElements = rUndoManager.LeaveAndMergeListAction();
564 else
565 nContextElements = rUndoManager.LeaveListAction();
567 const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 );
569 // prepare notification
570 void ( SAL_CALL XUndoManagerListener::*notificationMethod )( const UndoManagerEvent& ) = nullptr;
572 UndoManagerEvent aContextEvent( buildEvent( OUString() ) );
573 const EventObject aClearedEvent( getXUndoManager() );
574 if ( nContextElements == 0 )
576 notificationMethod = &XUndoManagerListener::cancelledContext;
578 else if ( isHiddenContext )
580 notificationMethod = &XUndoManagerListener::leftHiddenContext;
582 else
584 aContextEvent.UndoActionTitle = rUndoManager.GetUndoActionComment();
585 notificationMethod = &XUndoManagerListener::leftContext;
588 aGuard.clear();
589 // <--- SYNCHRONIZED
591 if ( bHadRedoActions && !bHasRedoActions )
592 m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aClearedEvent );
593 m_aUndoListeners.notifyEach( notificationMethod, aContextEvent );
594 impl_notifyModified();
597 void UndoManagerHelper_Impl::impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo )
599 ::osl::Guard< ::framework::IMutex > aExternalGuard( i_externalLock.getGuardedMutex() );
600 // note that this assumes that the mutex has been released in the thread which added the
601 // Undo/Redo request, so we can successfully acquire it
603 // SYNCHRONIZED --->
604 ::osl::ClearableMutexGuard aGuard( m_aMutex );
606 SfxUndoManager& rUndoManager = getUndoManager();
607 if ( rUndoManager.IsInListAction() )
608 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
610 const size_t nElements = i_undo
611 ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
612 : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
613 if ( nElements == 0 )
614 throw EmptyUndoStackException("stack is empty", getXUndoManager() );
616 aGuard.clear();
617 // <--- SYNCHRONIZED
621 if ( i_undo )
622 rUndoManager.Undo();
623 else
624 rUndoManager.Redo();
626 catch( const RuntimeException& ) { /* allowed to leave here */ throw; }
627 catch( const UndoFailedException& ) { /* allowed to leave here */ throw; }
628 catch( const Exception& )
630 // not allowed to leave
631 const Any aError( ::cppu::getCaughtException() );
632 throw UndoFailedException( OUString(), getXUndoManager(), aError );
635 // note that in opposite to all of the other methods, we do *not* have our mutex locked when calling
636 // into the SfxUndoManager implementation. This ensures that an actual XUndoAction::undo/redo is also
637 // called without our mutex being locked.
638 // As a consequence, we do not set m_bAPIActionRunning here. Instead, our actionUndone/actionRedone methods
639 // *always* multiplex the event to our XUndoManagerListeners, not only when m_bAPIActionRunning is FALSE (This
640 // again is different from all other SfxUndoListener methods).
641 // So, we do not need to do this notification here ourself.
644 void UndoManagerHelper_Impl::impl_addUndoAction( const Reference< XUndoAction >& i_action )
646 // SYNCHRONIZED --->
647 ::osl::ClearableMutexGuard aGuard( m_aMutex );
649 SfxUndoManager& rUndoManager = getUndoManager();
650 if ( !rUndoManager.IsUndoEnabled() )
651 // ignore the request if the manager is locked
652 return;
654 const UndoManagerEvent aEventAdd( buildEvent( i_action->getTitle() ) );
655 const EventObject aEventClear( getXUndoManager() );
657 const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount() > 0 );
659 ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
660 rUndoManager.AddUndoAction( std::make_unique<UndoActionWrapper>( i_action ) );
662 const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount() > 0 );
664 aGuard.clear();
665 // <--- SYNCHRONIZED
667 m_aUndoListeners.notifyEach( &XUndoManagerListener::undoActionAdded, aEventAdd );
668 if ( bHadRedoActions && !bHasRedoActions )
669 m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEventClear );
670 impl_notifyModified();
673 void UndoManagerHelper_Impl::impl_clear()
675 // SYNCHRONIZED --->
676 ::osl::ClearableMutexGuard aGuard( m_aMutex );
678 SfxUndoManager& rUndoManager = getUndoManager();
679 if ( rUndoManager.IsInListAction() )
680 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
683 ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
684 rUndoManager.Clear();
687 const EventObject aEvent( getXUndoManager() );
688 aGuard.clear();
689 // <--- SYNCHRONIZED
691 m_aUndoListeners.notifyEach( &XUndoManagerListener::allActionsCleared, aEvent );
692 impl_notifyModified();
695 void UndoManagerHelper_Impl::impl_clearRedo()
697 // SYNCHRONIZED --->
698 ::osl::ClearableMutexGuard aGuard( m_aMutex );
700 SfxUndoManager& rUndoManager = getUndoManager();
701 if ( rUndoManager.IsInListAction() )
702 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
705 ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
706 rUndoManager.ClearRedo();
709 const EventObject aEvent( getXUndoManager() );
710 aGuard.clear();
711 // <--- SYNCHRONIZED
713 m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEvent );
714 impl_notifyModified();
717 void UndoManagerHelper_Impl::impl_reset()
719 // SYNCHRONIZED --->
720 ::osl::ClearableMutexGuard aGuard( m_aMutex );
722 SfxUndoManager& rUndoManager = getUndoManager();
724 ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
725 rUndoManager.Reset();
728 const EventObject aEvent( getXUndoManager() );
729 aGuard.clear();
730 // <--- SYNCHRONIZED
732 m_aUndoListeners.notifyEach( &XUndoManagerListener::resetAll, aEvent );
733 impl_notifyModified();
736 void UndoManagerHelper_Impl::actionUndone( const OUString& i_actionComment )
738 UndoManagerEvent aEvent;
739 aEvent.Source = getXUndoManager();
740 aEvent.UndoActionTitle = i_actionComment;
741 aEvent.UndoContextDepth = 0; // Undo can happen on level 0 only
742 m_aUndoListeners.notifyEach( &XUndoManagerListener::actionUndone, aEvent );
743 impl_notifyModified();
746 void UndoManagerHelper_Impl::actionRedone( const OUString& i_actionComment )
748 UndoManagerEvent aEvent;
749 aEvent.Source = getXUndoManager();
750 aEvent.UndoActionTitle = i_actionComment;
751 aEvent.UndoContextDepth = 0; // Redo can happen on level 0 only
752 m_aUndoListeners.notifyEach( &XUndoManagerListener::actionRedone, aEvent );
753 impl_notifyModified();
756 void UndoManagerHelper_Impl::undoActionAdded( const OUString& i_actionComment )
758 if ( m_bAPIActionRunning )
759 return;
761 notify( i_actionComment, &XUndoManagerListener::undoActionAdded );
764 void UndoManagerHelper_Impl::cleared()
766 if ( m_bAPIActionRunning )
767 return;
769 notify( &XUndoManagerListener::allActionsCleared );
772 void UndoManagerHelper_Impl::clearedRedo()
774 if ( m_bAPIActionRunning )
775 return;
777 notify( &XUndoManagerListener::redoActionsCleared );
780 void UndoManagerHelper_Impl::resetAll()
782 if ( m_bAPIActionRunning )
783 return;
785 notify( &XUndoManagerListener::resetAll );
788 void UndoManagerHelper_Impl::listActionEntered( const OUString& i_comment )
790 #if OSL_DEBUG_LEVEL > 0
791 m_aContextAPIFlags.push( m_bAPIActionRunning );
792 #endif
794 if ( m_bAPIActionRunning )
795 return;
797 notify( i_comment, &XUndoManagerListener::enteredContext );
800 void UndoManagerHelper_Impl::listActionLeft( const OUString& i_comment )
802 #if OSL_DEBUG_LEVEL > 0
803 const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
804 m_aContextAPIFlags.pop();
805 OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" );
806 #endif
808 if ( m_bAPIActionRunning )
809 return;
811 notify( i_comment, &XUndoManagerListener::leftContext );
814 void UndoManagerHelper_Impl::listActionCancelled()
816 #if OSL_DEBUG_LEVEL > 0
817 const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
818 m_aContextAPIFlags.pop();
819 OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" );
820 #endif
822 if ( m_bAPIActionRunning )
823 return;
825 notify( OUString(), &XUndoManagerListener::cancelledContext );
828 //= UndoManagerHelper
830 UndoManagerHelper::UndoManagerHelper( IUndoManagerImplementation& i_undoManagerImpl )
831 :m_xImpl( new UndoManagerHelper_Impl( i_undoManagerImpl ) )
835 UndoManagerHelper::~UndoManagerHelper()
839 void UndoManagerHelper::disposing()
841 m_xImpl->disposing();
844 void UndoManagerHelper::enterUndoContext( const OUString& i_title, IMutexGuard& i_instanceLock )
846 m_xImpl->enterUndoContext( i_title, false, i_instanceLock );
849 void UndoManagerHelper::enterHiddenUndoContext( IMutexGuard& i_instanceLock )
851 m_xImpl->enterUndoContext( OUString(), true, i_instanceLock );
854 void UndoManagerHelper::leaveUndoContext( IMutexGuard& i_instanceLock )
856 m_xImpl->leaveUndoContext( i_instanceLock );
859 void UndoManagerHelper_Impl::undo( IMutexGuard& i_instanceLock )
861 impl_processRequest(
862 [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, true); },
863 i_instanceLock
867 void UndoManagerHelper_Impl::redo( IMutexGuard& i_instanceLock )
869 impl_processRequest(
870 [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, false); },
871 i_instanceLock
875 void UndoManagerHelper::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
877 m_xImpl->addUndoAction( i_action, i_instanceLock );
880 void UndoManagerHelper::undo( IMutexGuard& i_instanceLock )
882 m_xImpl->undo( i_instanceLock );
885 void UndoManagerHelper::redo( IMutexGuard& i_instanceLock )
887 m_xImpl->redo( i_instanceLock );
890 bool UndoManagerHelper::isUndoPossible() const
892 // SYNCHRONIZED --->
893 ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
894 SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
895 if ( rUndoManager.IsInListAction() )
896 return false;
897 return rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel ) > 0;
898 // <--- SYNCHRONIZED
901 bool UndoManagerHelper::isRedoPossible() const
903 // SYNCHRONIZED --->
904 ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
905 const SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
906 if ( rUndoManager.IsInListAction() )
907 return false;
908 return rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0;
909 // <--- SYNCHRONIZED
912 namespace
915 OUString lcl_getCurrentActionTitle( UndoManagerHelper_Impl& i_impl, const bool i_undo )
917 // SYNCHRONIZED --->
918 ::osl::MutexGuard aGuard( i_impl.getMutex() );
920 const SfxUndoManager& rUndoManager = i_impl.getUndoManager();
921 const size_t nActionCount = i_undo
922 ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
923 : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
924 if ( nActionCount == 0 )
925 throw EmptyUndoStackException(
926 i_undo ? OUString( "no action on the undo stack" )
927 : OUString( "no action on the redo stack" ),
928 i_impl.getXUndoManager()
930 return i_undo
931 ? rUndoManager.GetUndoActionComment( 0, SfxUndoManager::TopLevel )
932 : rUndoManager.GetRedoActionComment( 0, SfxUndoManager::TopLevel );
933 // <--- SYNCHRONIZED
936 Sequence< OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl, const bool i_undo )
938 // SYNCHRONIZED --->
939 ::osl::MutexGuard aGuard( i_impl.getMutex() );
941 const SfxUndoManager& rUndoManager = i_impl.getUndoManager();
942 const size_t nCount = i_undo
943 ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
944 : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
946 Sequence< OUString > aTitles( nCount );
947 for ( size_t i=0; i<nCount; ++i )
949 aTitles[i] = i_undo
950 ? rUndoManager.GetUndoActionComment( i, SfxUndoManager::TopLevel )
951 : rUndoManager.GetRedoActionComment( i, SfxUndoManager::TopLevel );
953 return aTitles;
954 // <--- SYNCHRONIZED
958 OUString UndoManagerHelper::getCurrentUndoActionTitle() const
960 return lcl_getCurrentActionTitle( *m_xImpl, true );
963 OUString UndoManagerHelper::getCurrentRedoActionTitle() const
965 return lcl_getCurrentActionTitle( *m_xImpl, false );
968 Sequence< OUString > UndoManagerHelper::getAllUndoActionTitles() const
970 return lcl_getAllActionTitles( *m_xImpl, true );
973 Sequence< OUString > UndoManagerHelper::getAllRedoActionTitles() const
975 return lcl_getAllActionTitles( *m_xImpl, false );
978 void UndoManagerHelper::clear( IMutexGuard& i_instanceLock )
980 m_xImpl->clear( i_instanceLock );
983 void UndoManagerHelper::clearRedo( IMutexGuard& i_instanceLock )
985 m_xImpl->clearRedo( i_instanceLock );
988 void UndoManagerHelper::reset( IMutexGuard& i_instanceLock )
990 m_xImpl->reset( i_instanceLock );
993 void UndoManagerHelper::lock()
995 m_xImpl->lock();
998 void UndoManagerHelper::unlock()
1000 m_xImpl->unlock();
1003 bool UndoManagerHelper::isLocked()
1005 // SYNCHRONIZED --->
1006 ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
1008 SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
1009 return !rUndoManager.IsUndoEnabled();
1010 // <--- SYNCHRONIZED
1013 void UndoManagerHelper::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
1015 if ( i_listener.is() )
1016 m_xImpl->addUndoManagerListener( i_listener );
1019 void UndoManagerHelper::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
1021 if ( i_listener.is() )
1022 m_xImpl->removeUndoManagerListener( i_listener );
1025 void UndoManagerHelper::addModifyListener( const Reference< XModifyListener >& i_listener )
1027 if ( i_listener.is() )
1028 m_xImpl->addModifyListener( i_listener );
1031 void UndoManagerHelper::removeModifyListener( const Reference< XModifyListener >& i_listener )
1033 if ( i_listener.is() )
1034 m_xImpl->removeModifyListener( i_listener );
1037 } // namespace framework
1039 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */