1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
;
70 class UndoActionWrapper
: public SfxUndoAction
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
;
84 const Reference
< XUndoAction
> m_xUndoAction
;
87 UndoActionWrapper::UndoActionWrapper( Reference
< XUndoAction
> const& i_undoAction
)
89 ,m_xUndoAction( i_undoAction
)
91 ENSURE_OR_THROW( m_xUndoAction
.is(), "illegal undo action" );
94 UndoActionWrapper::~UndoActionWrapper()
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
113 sComment
= m_xUndoAction
->getTitle();
115 catch( const Exception
& )
117 DBG_UNHANDLED_EXCEPTION("fwk");
122 void UndoActionWrapper::Undo()
124 m_xUndoAction
->undo();
127 void UndoActionWrapper::Redo()
129 m_xUndoAction
->redo();
132 bool UndoActionWrapper::CanRepeat(SfxRepeatTarget
&) const
137 //= UndoManagerRequest
139 class UndoManagerRequest
: public ::comphelper::AnyEvent
142 explicit UndoManagerRequest( ::std::function
<void ()> const& i_request
)
143 :m_request( i_request
)
147 m_finishCondition
.reset();
156 catch( const Exception
& )
158 m_caughtException
= ::cppu::getCaughtException();
160 m_finishCondition
.set();
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.",
176 m_finishCondition
.set();
180 virtual ~UndoManagerRequest() override
185 ::std::function
<void ()> m_request
;
186 Any m_caughtException
;
187 ::osl::Condition m_finishCondition
;
190 //= UndoManagerHelper_Impl
192 class UndoManagerHelper_Impl
: public SfxUndoListener
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
;
207 ::std::queue
< ::rtl::Reference
< UndoManagerRequest
> >
211 ::osl::Mutex
& getMutex() { return m_aMutex
; }
214 explicit UndoManagerHelper_Impl( IUndoManagerImplementation
& i_undoManagerImpl
)
217 ,m_bAPIActionRunning( false )
218 ,m_bProcessingEvents( false )
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();
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
;
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
);
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
);
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
& ) );
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
);
306 void impl_clearRedo();
310 void UndoManagerHelper_Impl::disposing()
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();
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
)
365 [this, &i_title
, i_hidden
] () { return this->impl_enterUndoContext(i_title
, i_hidden
); },
370 void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard
& i_instanceLock
)
373 [this] () { return this->impl_leaveUndoContext(); },
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",
388 [this, &i_action
] () { return this->impl_addUndoAction(i_action
); },
393 void UndoManagerHelper_Impl::clear( IMutexGuard
& i_instanceLock
)
396 [this] () { return this->impl_clear(); },
401 void UndoManagerHelper_Impl::clearRedo( IMutexGuard
& i_instanceLock
)
404 [this] () { return this->impl_clearRedo(); },
409 void UndoManagerHelper_Impl::reset( IMutexGuard
& i_instanceLock
)
412 [this] () { return this->impl_reset(); },
417 void UndoManagerHelper_Impl::lock()
420 ::osl::MutexGuard
aGuard( getMutex() );
422 if ( ++m_nLockCount
== 1 )
424 SfxUndoManager
& rUndoManager
= getUndoManager();
425 rUndoManager
.EnableUndo( false );
430 void UndoManagerHelper_Impl::unlock()
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 );
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
464 m_bProcessingEvents
= true;
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;
478 pRequest
= m_aEventQueue
.front();
489 // no chance to process further requests, if the current one failed
491 ::osl::MutexGuard
aQueueGuard( m_aQueueMutex
);
492 while ( !m_aEventQueue
.empty() )
494 pRequest
= m_aEventQueue
.front();
496 pRequest
->cancel( getXUndoManager() );
498 m_bProcessingEvents
= false;
500 // re-throw the error
507 void UndoManagerHelper_Impl::impl_enterUndoContext( const OUString
& i_title
, const bool i_hidden
)
510 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
512 SfxUndoManager
& rUndoManager
= getUndoManager();
513 if ( !rUndoManager
.IsUndoEnabled() )
514 // ignore this request if the manager is locked
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
) );
534 m_aUndoListeners
.notifyEach( i_hidden
? &XUndoManagerListener::enteredHiddenContext
: &XUndoManagerListener::enteredContext
, aEvent
);
535 impl_notifyModified();
538 void UndoManagerHelper_Impl::impl_leaveUndoContext()
541 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
543 SfxUndoManager
& rUndoManager
= getUndoManager();
544 if ( !rUndoManager
.IsUndoEnabled() )
545 // ignore this request if the manager is locked
548 if ( !rUndoManager
.IsInListAction() )
549 throw InvalidStateException(
550 "no active undo context",
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();
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
;
584 aContextEvent
.UndoActionTitle
= rUndoManager
.GetUndoActionComment();
585 notificationMethod
= &XUndoManagerListener::leftContext
;
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
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() );
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
)
647 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
649 SfxUndoManager
& rUndoManager
= getUndoManager();
650 if ( !rUndoManager
.IsUndoEnabled() )
651 // ignore the request if the manager is locked
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 );
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()
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() );
691 m_aUndoListeners
.notifyEach( &XUndoManagerListener::allActionsCleared
, aEvent
);
692 impl_notifyModified();
695 void UndoManagerHelper_Impl::impl_clearRedo()
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() );
713 m_aUndoListeners
.notifyEach( &XUndoManagerListener::redoActionsCleared
, aEvent
);
714 impl_notifyModified();
717 void UndoManagerHelper_Impl::impl_reset()
720 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
722 SfxUndoManager
& rUndoManager
= getUndoManager();
724 ::comphelper::FlagGuard
aNotificationGuard( m_bAPIActionRunning
);
725 rUndoManager
.Reset();
728 const EventObject
aEvent( getXUndoManager() );
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
)
761 notify( i_actionComment
, &XUndoManagerListener::undoActionAdded
);
764 void UndoManagerHelper_Impl::cleared()
766 if ( m_bAPIActionRunning
)
769 notify( &XUndoManagerListener::allActionsCleared
);
772 void UndoManagerHelper_Impl::clearedRedo()
774 if ( m_bAPIActionRunning
)
777 notify( &XUndoManagerListener::redoActionsCleared
);
780 void UndoManagerHelper_Impl::resetAll()
782 if ( m_bAPIActionRunning
)
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
);
794 if ( m_bAPIActionRunning
)
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!" );
808 if ( m_bAPIActionRunning
)
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!" );
822 if ( m_bAPIActionRunning
)
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
)
862 [this, &i_instanceLock
] () { return this->impl_doUndoRedo(i_instanceLock
, true); },
867 void UndoManagerHelper_Impl::redo( IMutexGuard
& i_instanceLock
)
870 [this, &i_instanceLock
] () { return this->impl_doUndoRedo(i_instanceLock
, false); },
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
893 ::osl::MutexGuard
aGuard( m_xImpl
->getMutex() );
894 SfxUndoManager
& rUndoManager
= m_xImpl
->getUndoManager();
895 if ( rUndoManager
.IsInListAction() )
897 return rUndoManager
.GetUndoActionCount( SfxUndoManager::TopLevel
) > 0;
901 bool UndoManagerHelper::isRedoPossible() const
904 ::osl::MutexGuard
aGuard( m_xImpl
->getMutex() );
905 const SfxUndoManager
& rUndoManager
= m_xImpl
->getUndoManager();
906 if ( rUndoManager
.IsInListAction() )
908 return rUndoManager
.GetRedoActionCount( SfxUndoManager::TopLevel
) > 0;
915 OUString
lcl_getCurrentActionTitle( UndoManagerHelper_Impl
& i_impl
, const bool i_undo
)
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()
931 ? rUndoManager
.GetUndoActionComment( 0, SfxUndoManager::TopLevel
)
932 : rUndoManager
.GetRedoActionComment( 0, SfxUndoManager::TopLevel
);
936 Sequence
< OUString
> lcl_getAllActionTitles( UndoManagerHelper_Impl
& i_impl
, const bool i_undo
)
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
)
950 ? rUndoManager
.GetUndoActionComment( i
, SfxUndoManager::TopLevel
)
951 : rUndoManager
.GetRedoActionComment( i
, SfxUndoManager::TopLevel
);
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()
998 void UndoManagerHelper::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: */