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>
38#include <osl/conditn.hxx>
50 using ::com::sun::star::uno::Reference;
51 using ::com::sun::star::uno::XInterface;
52 using ::com::sun::star::uno::UNO_QUERY;
53 using ::com::sun::star::uno::Exception;
54 using ::com::sun::star::uno::RuntimeException;
55 using ::com::sun::star::uno::Any;
56 using ::com::sun::star::uno::Sequence;
57 using ::com::sun::star::document::XUndoManagerListener;
58 using ::com::sun::star::document::UndoManagerEvent;
59 using ::com::sun::star::document::EmptyUndoStackException;
60 using ::com::sun::star::document::UndoContextNotClosedException;
61 using ::com::sun::star::document::UndoFailedException;
62 using ::com::sun::star::util::NotLockedException;
63 using ::com::sun::star::lang::EventObject;
64 using ::com::sun::star::document::XUndoAction;
65 using ::com::sun::star::lang::XComponent;
66 using ::com::sun::star::document::XUndoManager;
67 using ::com::sun::star::util::InvalidStateException;
68 using ::com::sun::star::lang::IllegalArgumentException;
69 using ::com::sun::star::util::XModifyListener;
78 explicit UndoActionWrapper(
79 Reference< XUndoAction >
const& i_undoAction
81 virtual ~UndoActionWrapper()
override;
83 virtual OUString GetComment()
const override;
84 virtual void Undo()
override;
85 virtual void Redo()
override;
86 virtual bool CanRepeat(SfxRepeatTarget&)
const override;
94 UndoActionWrapper::UndoActionWrapper( Reference< XUndoAction >
const& i_undoAction )
100 UndoActionWrapper::~UndoActionWrapper()
104 Reference< XComponent > xComponent(
m_xUndoAction, UNO_QUERY );
105 if ( xComponent.is() )
106 xComponent->dispose();
108 catch(
const Exception& )
114 OUString UndoActionWrapper::GetComment()
const
121 catch(
const Exception& )
128 void UndoActionWrapper::Undo()
133 void UndoActionWrapper::Redo()
138 bool UndoActionWrapper::CanRepeat(SfxRepeatTarget&)
const
150 explicit UndoManagerRequest( ::std::function<
void ()> i_request )
162 catch(
const Exception& )
176 void cancel(
const Reference< XInterface >& i_context )
179 "Concurrency error: an earlier operation on the stack failed.",
186 virtual ~UndoManagerRequest()
override
214#if OSL_DEBUG_LEVEL > 0
215 bool m_bContextAPIFlagsEverPushed = {
false};
218 ::std::queue< ::rtl::Reference< UndoManagerRequest > >
226 :m_bAPIActionRunning( false )
227 ,m_bProcessingEvents( false )
229 ,m_rUndoManagerImplementation( i_undoManagerImpl )
231 getUndoManager().AddUndoListener( *
this );
245 return m_rUndoManagerImplementation.
getThis();
249 virtual void actionUndone(
const OUString& i_actionComment )
override;
250 virtual void actionRedone(
const OUString& i_actionComment )
override;
251 virtual void undoActionAdded(
const OUString& i_actionComment )
override;
252 virtual void cleared()
override;
253 virtual void clearedRedo()
override;
254 virtual void resetAll()
override;
255 virtual void listActionEntered(
const OUString& i_comment )
override;
256 virtual void listActionLeft(
const OUString& i_comment )
override;
257 virtual void listActionCancelled()
override;
262 void enterUndoContext(
const OUString& i_title,
const bool i_hidden,
IMutexGuard& i_instanceLock );
263 void leaveUndoContext(
IMutexGuard& i_instanceLock );
264 void addUndoAction(
const Reference< XUndoAction >& i_action,
IMutexGuard& i_instanceLock );
276 std::unique_lock g(m_aListenerMutex);
282 std::unique_lock g(m_aListenerMutex);
288 std::unique_lock g(m_aListenerMutex);
294 std::unique_lock g(m_aListenerMutex);
299 buildEvent( OUString
const& i_title )
const;
301 void impl_notifyModified();
302 void notify( OUString
const& i_title,
303 void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )(
const UndoManagerEvent& )
305 void notify(
void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )(
const EventObject& ) );
309 void impl_processRequest(::std::function<
void ()>
const& i_request,
IMutexGuard& i_instanceLock);
312 void impl_enterUndoContext(
const OUString& i_title,
const bool i_hidden );
313 void impl_leaveUndoContext();
314 void impl_addUndoAction(
const Reference< XUndoAction >& i_action );
315 void impl_doUndoRedo(
IMutexGuard& i_externalLock,
const bool i_undo );
317 void impl_clearRedo();
321 void UndoManagerHelper_Impl::disposing()
324 aEvent.Source = getXUndoManager();
326 std::unique_lock g(m_aListenerMutex);
330 ::osl::MutexGuard aGuard(
m_aMutex );
332 getUndoManager().RemoveUndoListener( *
this );
335 UndoManagerEvent UndoManagerHelper_Impl::buildEvent( OUString
const& i_title )
const
338 aEvent.Source = getXUndoManager();
339 aEvent.UndoActionTitle = i_title;
340 aEvent.UndoContextDepth = getUndoManager().GetListActionDepth();
344 void UndoManagerHelper_Impl::impl_notifyModified()
346 const EventObject
aEvent( getXUndoManager() );
347 std::unique_lock g(m_aListenerMutex);
351 void UndoManagerHelper_Impl::notify( OUString
const& i_title,
352 void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )(
const UndoManagerEvent& ) )
354 const UndoManagerEvent
aEvent( buildEvent( i_title ) );
363 std::unique_lock g(m_aListenerMutex);
366 impl_notifyModified();
369 void UndoManagerHelper_Impl::notify(
void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )(
const EventObject& ) )
371 const EventObject
aEvent( getXUndoManager() );
375 std::unique_lock g(m_aListenerMutex);
378 impl_notifyModified();
381 void UndoManagerHelper_Impl::enterUndoContext(
const OUString& i_title,
const bool i_hidden,
IMutexGuard& i_instanceLock )
384 [
this, &i_title, i_hidden] () {
return this->impl_enterUndoContext(i_title, i_hidden); },
389 void UndoManagerHelper_Impl::leaveUndoContext(
IMutexGuard& i_instanceLock )
392 [
this] () {
return this->impl_leaveUndoContext(); },
397 void UndoManagerHelper_Impl::addUndoAction(
const Reference< XUndoAction >& i_action,
IMutexGuard& i_instanceLock )
399 if ( !i_action.is() )
400 throw IllegalArgumentException(
401 "illegal undo action object",
407 [
this, &i_action] () {
return this->impl_addUndoAction(i_action); },
415 [
this] () {
return this->impl_clear(); },
420 void UndoManagerHelper_Impl::clearRedo(
IMutexGuard& i_instanceLock )
423 [
this] () {
return this->impl_clearRedo(); },
431 [
this] () {
return this->impl_reset(); },
436 void UndoManagerHelper_Impl::lock()
439 ::osl::MutexGuard aGuard(
getMutex() );
441 if ( ++m_nLockCount == 1 )
449 void UndoManagerHelper_Impl::unlock()
452 ::osl::MutexGuard aGuard(
getMutex() );
454 if ( m_nLockCount == 0 )
455 throw NotLockedException(
"Undo manager is not locked", getXUndoManager() );
457 if ( --m_nLockCount == 0 )
465 void UndoManagerHelper_Impl::impl_processRequest(::std::function<
void ()>
const& i_request,
IMutexGuard& i_instanceLock)
470 std::unique_lock aQueueGuard( m_aQueueMutex );
471 m_aEventQueue.push( pRequest );
474 i_instanceLock.
clear();
476 if ( m_bProcessingEvents )
483 m_bProcessingEvents =
true;
488 std::unique_lock aQueueGuard( m_aQueueMutex );
489 if ( m_aEventQueue.empty() )
494 m_bProcessingEvents =
false;
497 pRequest = m_aEventQueue.front();
510 std::unique_lock aQueueGuard( m_aQueueMutex );
511 while ( !m_aEventQueue.empty() )
513 pRequest = m_aEventQueue.front();
515 pRequest->cancel( getXUndoManager() );
517 m_bProcessingEvents =
false;
526 void UndoManagerHelper_Impl::impl_enterUndoContext(
const OUString& i_title,
const bool i_hidden )
529 ::osl::ClearableMutexGuard aGuard(
m_aMutex );
537 throw EmptyUndoStackException(
538 "can't enter a hidden context without a previous Undo action",
539 m_rUndoManagerImplementation.
getThis()
547 m_aContextVisibilities.push( i_hidden );
549 const UndoManagerEvent
aEvent( buildEvent( i_title ) );
554 std::unique_lock g(m_aListenerMutex);
555 m_aUndoListeners.
notifyEach( g, i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext,
aEvent );
557 impl_notifyModified();
560 void UndoManagerHelper_Impl::impl_leaveUndoContext()
563 ::osl::ClearableMutexGuard aGuard(
m_aMutex );
571 throw InvalidStateException(
572 "no active undo context",
576 size_t nContextElements = 0;
578 const bool isHiddenContext = m_aContextVisibilities.top();
579 m_aContextVisibilities.pop();
584 if ( isHiddenContext )
592 void ( SAL_CALL XUndoManagerListener::*notificationMethod )(
const UndoManagerEvent& ) =
nullptr;
594 UndoManagerEvent aContextEvent( buildEvent( OUString() ) );
595 const EventObject aClearedEvent( getXUndoManager() );
596 if ( nContextElements == 0 )
598 notificationMethod = &XUndoManagerListener::cancelledContext;
600 else if ( isHiddenContext )
602 notificationMethod = &XUndoManagerListener::leftHiddenContext;
607 notificationMethod = &XUndoManagerListener::leftContext;
614 std::unique_lock g(m_aListenerMutex);
615 if ( bHadRedoActions && !bHasRedoActions )
616 m_aUndoListeners.
notifyEach( g, &XUndoManagerListener::redoActionsCleared, aClearedEvent );
617 m_aUndoListeners.
notifyEach( g, notificationMethod, aContextEvent );
619 impl_notifyModified();
622 void UndoManagerHelper_Impl::impl_doUndoRedo(
IMutexGuard& i_externalLock,
const bool i_undo )
624 ::osl::Guard< ::framework::IMutex > aExternalGuard( i_externalLock.
getGuardedMutex() );
629 ::osl::ClearableMutexGuard aGuard(
m_aMutex );
633 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
639 throw EmptyUndoStackException(
"stack is empty", getXUndoManager() );
652 catch(
const UndoFailedException& ) {
throw; }
656 const Any aError( ::cppu::getCaughtException() );
657 throw UndoFailedException( OUString(), getXUndoManager(), aError );
669 void UndoManagerHelper_Impl::impl_addUndoAction(
const Reference< XUndoAction >& i_action )
672 ::osl::ClearableMutexGuard aGuard(
m_aMutex );
679 const UndoManagerEvent aEventAdd( buildEvent( i_action->getTitle() ) );
680 const EventObject aEventClear( getXUndoManager() );
685 rUndoManager.
AddUndoAction( std::make_unique<UndoActionWrapper>( i_action ) );
693 std::unique_lock g(m_aListenerMutex);
694 m_aUndoListeners.
notifyEach( g, &XUndoManagerListener::undoActionAdded, aEventAdd );
695 if ( bHadRedoActions && !bHasRedoActions )
696 m_aUndoListeners.
notifyEach( g, &XUndoManagerListener::redoActionsCleared, aEventClear );
698 impl_notifyModified();
701 void UndoManagerHelper_Impl::impl_clear()
706 ::osl::MutexGuard aGuard2(
m_aMutex );
710 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
714 rUndoManager.
Clear();
717 aEvent = EventObject( getXUndoManager() );
721 std::unique_lock g(m_aListenerMutex);
722 m_aUndoListeners.
notifyEach( g, &XUndoManagerListener::allActionsCleared,
aEvent );
724 impl_notifyModified();
727 void UndoManagerHelper_Impl::impl_clearRedo()
730 ::osl::ClearableMutexGuard aGuard(
m_aMutex );
734 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
741 const EventObject
aEvent( getXUndoManager() );
746 std::unique_lock g(m_aListenerMutex);
747 m_aUndoListeners.
notifyEach( g, &XUndoManagerListener::redoActionsCleared,
aEvent );
749 impl_notifyModified();
752 void UndoManagerHelper_Impl::impl_reset()
755 ::osl::ClearableMutexGuard aGuard(
m_aMutex );
760 rUndoManager.
Reset();
763 const EventObject
aEvent( getXUndoManager() );
768 std::unique_lock g(m_aListenerMutex);
771 impl_notifyModified();
774 void UndoManagerHelper_Impl::actionUndone(
const OUString& i_actionComment )
777 aEvent.Source = getXUndoManager();
778 aEvent.UndoActionTitle = i_actionComment;
779 aEvent.UndoContextDepth = 0;
781 std::unique_lock g(m_aListenerMutex);
782 m_aUndoListeners.
notifyEach( g, &XUndoManagerListener::actionUndone,
aEvent );
784 impl_notifyModified();
787 void UndoManagerHelper_Impl::actionRedone(
const OUString& i_actionComment )
790 aEvent.Source = getXUndoManager();
791 aEvent.UndoActionTitle = i_actionComment;
792 aEvent.UndoContextDepth = 0;
794 std::unique_lock g(m_aListenerMutex);
795 m_aUndoListeners.
notifyEach( g, &XUndoManagerListener::actionRedone,
aEvent );
797 impl_notifyModified();
800 void UndoManagerHelper_Impl::undoActionAdded(
const OUString& i_actionComment )
802 if ( m_bAPIActionRunning )
805 notify( i_actionComment, &XUndoManagerListener::undoActionAdded );
808 void UndoManagerHelper_Impl::cleared()
810 if ( m_bAPIActionRunning )
813 notify( &XUndoManagerListener::allActionsCleared );
816 void UndoManagerHelper_Impl::clearedRedo()
818 if ( m_bAPIActionRunning )
821 notify( &XUndoManagerListener::redoActionsCleared );
824 void UndoManagerHelper_Impl::resetAll()
826 if ( m_bAPIActionRunning )
829 notify( &XUndoManagerListener::resetAll );
832 void UndoManagerHelper_Impl::listActionEntered(
const OUString& i_comment )
834#if OSL_DEBUG_LEVEL > 0
835 m_aContextAPIFlags.push( m_bAPIActionRunning );
836 m_bContextAPIFlagsEverPushed =
true;
839 if ( m_bAPIActionRunning )
842 notify( i_comment, &XUndoManagerListener::enteredContext );
845 void UndoManagerHelper_Impl::listActionLeft(
const OUString& i_comment )
847#if OSL_DEBUG_LEVEL > 0
854 if (m_bContextAPIFlagsEverPushed)
856 const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
857 m_aContextAPIFlags.pop();
858 OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning,
"UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" );
862 if ( m_bAPIActionRunning )
865 notify( i_comment, &XUndoManagerListener::leftContext );
868 void UndoManagerHelper_Impl::listActionCancelled()
870#if OSL_DEBUG_LEVEL > 0
871 const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
872 m_aContextAPIFlags.pop();
873 OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning,
"UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" );
876 if ( m_bAPIActionRunning )
879 notify( OUString(), &XUndoManagerListener::cancelledContext );
900 m_xImpl->enterUndoContext( i_title,
false, i_instanceLock );
905 m_xImpl->enterUndoContext( OUString(),
true, i_instanceLock );
910 m_xImpl->leaveUndoContext( i_instanceLock );
916 [
this, &i_instanceLock] () {
return this->
impl_doUndoRedo(i_instanceLock,
true); },
924 [
this, &i_instanceLock] () {
return this->
impl_doUndoRedo(i_instanceLock,
false); },
931 m_xImpl->addUndoAction( i_action, i_instanceLock );
936 m_xImpl->undo( i_instanceLock );
941 m_xImpl->redo( i_instanceLock );
947 ::osl::MutexGuard aGuard(
m_xImpl->getMutex() );
958 ::osl::MutexGuard aGuard(
m_xImpl->getMutex() );
972 ::osl::MutexGuard aGuard( i_impl.
getMutex() );
975 const size_t nActionCount = i_undo
978 if ( nActionCount == 0 )
979 throw EmptyUndoStackException(
980 i_undo ? OUString(
"no action on the undo stack" )
981 : OUString(
"no action on the redo stack" ),
990 Sequence< OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl,
const bool i_undo )
993 ::osl::MutexGuard aGuard( i_impl.getMutex() );
996 const size_t nCount = i_undo
1000 Sequence< OUString > aTitles( nCount );
1001 auto aTitlesRange = asNonConstRange(aTitles);
1004 aTitlesRange[
i] = i_undo
1015 return lcl_getCurrentActionTitle( *
m_xImpl,
true );
1020 return lcl_getCurrentActionTitle( *
m_xImpl,
false );
1025 return lcl_getAllActionTitles( *
m_xImpl,
true );
1030 return lcl_getAllActionTitles( *
m_xImpl,
false );
1035 m_xImpl->clear( i_instanceLock );
1040 m_xImpl->clearRedo( i_instanceLock );
1045 m_xImpl->reset( i_instanceLock );
1061 ::osl::MutexGuard aGuard(
m_xImpl->getMutex() );
1070 if ( i_listener.is() )
1071 m_xImpl->addUndoManagerListener( i_listener );
1076 if ( i_listener.is() )
1077 m_xImpl->removeUndoManagerListener( i_listener );
1082 if ( i_listener.is() )
1083 m_xImpl->addModifyListener( i_listener );
1088 if ( i_listener.is() )
1089 m_xImpl->removeModifyListener( i_listener );
void EnableUndo(bool bEnable)
OUString GetRedoActionComment(size_t nNo=0, bool const i_currentLevel=CurrentLevel) const
bool IsInListAction() const
size_t LeaveAndMergeListAction()
OUString GetUndoActionComment(size_t nNo=0, bool const i_currentLevel=CurrentLevel) const
static bool const TopLevel
virtual void EnterListAction(const OUString &rComment, const OUString &rRepeatComment, sal_uInt16 nId, ViewShellId nViewShellId)
virtual size_t GetRedoActionCount(bool const i_currentLevel=CurrentLevel) const
bool IsUndoEnabled() const
virtual void AddUndoAction(std::unique_ptr< SfxUndoAction > pAction, bool bTryMerg=false)
virtual size_t GetUndoActionCount(bool const i_currentLevel=CurrentLevel) const
sal_Int32 addInterface(std::unique_lock< std::mutex > &rGuard, const css::uno::Reference< ListenerT > &rxIFace)
void notifyEach(std::unique_lock< std::mutex > &rGuard, void(SAL_CALL ListenerT::*NotificationMethod)(const EventT &), const EventT &Event) const
void disposeAndClear(::std::unique_lock<::std::mutex > &rGuard, const css::lang::EventObject &rEvt)
sal_Int32 removeInterface(std::unique_lock< std::mutex > &rGuard, const css::uno::Reference< ListenerT > &rxIFace)
virtual void clear()=0
clears the lock.
virtual IMutex & getGuardedMutex()=0
returns the mutex guarded by the instance.
virtual css::uno::Reference< css::document::XUndoManager > getThis()=0
provides access to a UNO interface for the XUndoManager implementation.
virtual SfxUndoManager & getImplUndoManager()=0
returns the SfxUndoManager interface to the actual Undo stack
std::mutex m_aListenerMutex
Use different mutex for listeners to prevent ABBA deadlocks.
::std::queue< ::rtl::Reference< UndoManagerRequest > > m_aEventQueue
virtual ~UndoManagerHelper_Impl()
void removeUndoManagerListener(const Reference< XUndoManagerListener > &i_listener)
::std::stack< bool > m_aContextVisibilities
SfxUndoManager & getUndoManager() const
UndoManagerHelper_Impl(IUndoManagerImplementation &i_undoManagerImpl)
Reference< XUndoManager > getXUndoManager() const
::comphelper::OInterfaceContainerHelper4< XModifyListener > m_aModifyListeners
void removeModifyListener(const Reference< XModifyListener > &i_listener)
void addModifyListener(const Reference< XModifyListener > &i_listener)
void impl_doUndoRedo(IMutexGuard &i_externalLock, const bool i_undo)
void redo(IMutexGuard &i_instanceLock)
::osl::Mutex & getMutex()
::std::stack< bool > m_aContextAPIFlags
IUndoManagerImplementation & m_rUndoManagerImplementation
void undo(IMutexGuard &i_instanceLock)
::comphelper::OInterfaceContainerHelper4< XUndoManagerListener > m_aUndoListeners
void impl_processRequest(::std::function< void()> const &i_request, IMutexGuard &i_instanceLock)
adds a function to be called to the request processor's queue
void addUndoManagerListener(const Reference< XUndoManagerListener > &i_listener)
void removeModifyListener(const css::uno::Reference< css::util::XModifyListener > &i_listener)
bool isUndoPossible() const
void leaveUndoContext(IMutexGuard &i_instanceLock)
void reset(IMutexGuard &i_instanceLock)
void enterUndoContext(const OUString &i_title, IMutexGuard &i_instanceLock)
css::uno::Sequence< OUString > getAllRedoActionTitles() const
void clearRedo(IMutexGuard &i_instanceLock)
void removeUndoManagerListener(const css::uno::Reference< css::document::XUndoManagerListener > &i_listener)
bool isRedoPossible() const
void enterHiddenUndoContext(IMutexGuard &i_instanceLock)
css::uno::Sequence< OUString > getAllUndoActionTitles() const
std::unique_ptr< UndoManagerHelper_Impl > m_xImpl
void clear(IMutexGuard &i_instanceLock)
OUString getCurrentRedoActionTitle() const
void redo(IMutexGuard &i_instanceLock)
void addModifyListener(const css::uno::Reference< css::util::XModifyListener > &i_listener)
void undo(IMutexGuard &i_instanceLock)
void addUndoAction(const css::uno::Reference< css::document::XUndoAction > &i_action, IMutexGuard &i_instanceLock)
void addUndoManagerListener(const css::uno::Reference< css::document::XUndoManagerListener > &i_listener)
OUString getCurrentUndoActionTitle() const
#define ENSURE_OR_THROW(c, m)
#define DBG_UNHANDLED_EXCEPTION(...)
std::shared_ptr< osl::Mutex > const & lock()
::osl::Mutex & getMutex()
::std::function< void()> m_request
const Reference< XUndoAction > m_xUndoAction
::osl::Condition m_finishCondition