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>
22 #include <com/sun/star/lang/XComponent.hpp>
24 #include <cppuhelper/interfacecontainer.hxx>
25 #include <cppuhelper/exc_hlp.hxx>
26 #include <comphelper/flagguard.hxx>
27 #include <comphelper/asyncnotification.hxx>
28 #include <svl/undo.hxx>
29 #include <tools/diagnose_ex.h>
30 #include <osl/conditn.hxx>
34 #include <boost/function.hpp>
39 using ::com::sun::star::uno::Reference
;
40 using ::com::sun::star::uno::XInterface
;
41 using ::com::sun::star::uno::UNO_QUERY
;
42 using ::com::sun::star::uno::UNO_QUERY_THROW
;
43 using ::com::sun::star::uno::UNO_SET_THROW
;
44 using ::com::sun::star::uno::Exception
;
45 using ::com::sun::star::uno::RuntimeException
;
46 using ::com::sun::star::uno::Any
;
47 using ::com::sun::star::uno::makeAny
;
48 using ::com::sun::star::uno::Sequence
;
49 using ::com::sun::star::uno::Type
;
50 using ::com::sun::star::document::XUndoManagerListener
;
51 using ::com::sun::star::document::UndoManagerEvent
;
52 using ::com::sun::star::document::EmptyUndoStackException
;
53 using ::com::sun::star::document::UndoContextNotClosedException
;
54 using ::com::sun::star::document::UndoFailedException
;
55 using ::com::sun::star::util::NotLockedException
;
56 using ::com::sun::star::lang::EventObject
;
57 using ::com::sun::star::document::XUndoAction
;
58 using ::com::sun::star::lang::XComponent
;
59 using ::com::sun::star::document::XUndoManager
;
60 using ::com::sun::star::util::InvalidStateException
;
61 using ::com::sun::star::lang::IllegalArgumentException
;
62 using ::com::sun::star::util::XModifyListener
;
63 using ::svl::IUndoManager
;
67 class UndoActionWrapper
: public SfxUndoAction
71 Reference
< XUndoAction
> const& i_undoAction
73 virtual ~UndoActionWrapper();
75 virtual OUString
GetComment() const SAL_OVERRIDE
;
76 virtual void Undo() SAL_OVERRIDE
;
77 virtual void Redo() SAL_OVERRIDE
;
78 virtual bool CanRepeat(SfxRepeatTarget
&) const SAL_OVERRIDE
;
81 const Reference
< XUndoAction
> m_xUndoAction
;
84 UndoActionWrapper::UndoActionWrapper( Reference
< XUndoAction
> const& i_undoAction
)
86 ,m_xUndoAction( i_undoAction
)
88 ENSURE_OR_THROW( m_xUndoAction
.is(), "illegal undo action" );
91 UndoActionWrapper::~UndoActionWrapper()
95 Reference
< XComponent
> xComponent( m_xUndoAction
, UNO_QUERY
);
96 if ( xComponent
.is() )
97 xComponent
->dispose();
99 catch( const Exception
& )
101 DBG_UNHANDLED_EXCEPTION();
105 OUString
UndoActionWrapper::GetComment() const
110 sComment
= m_xUndoAction
->getTitle();
112 catch( const Exception
& )
114 DBG_UNHANDLED_EXCEPTION();
119 void UndoActionWrapper::Undo()
121 m_xUndoAction
->undo();
124 void UndoActionWrapper::Redo()
126 m_xUndoAction
->redo();
129 bool UndoActionWrapper::CanRepeat(SfxRepeatTarget
&) const
134 //= UndoManagerRequest
136 class UndoManagerRequest
: public ::comphelper::AnyEvent
139 UndoManagerRequest( ::boost::function0
< void > const& i_request
)
140 :m_request( i_request
)
144 m_finishCondition
.reset();
153 catch( const Exception
& )
155 m_caughtException
= ::cppu::getCaughtException();
157 m_finishCondition
.set();
162 m_finishCondition
.wait();
163 if ( m_caughtException
.hasValue() )
164 ::cppu::throwException( m_caughtException
);
167 void cancel( const Reference
< XInterface
>& i_context
)
169 m_caughtException
<<= RuntimeException(
170 OUString( "Concurrency error: an earlier operation on the stack failed." ),
173 m_finishCondition
.set();
177 virtual ~UndoManagerRequest()
182 ::boost::function0
< void > m_request
;
183 Any m_caughtException
;
184 ::osl::Condition m_finishCondition
;
187 //= UndoManagerHelper_Impl
189 class UndoManagerHelper_Impl
: public SfxUndoListener
192 ::osl::Mutex m_aMutex
;
193 ::osl::Mutex m_aQueueMutex
;
195 bool m_bAPIActionRunning
;
196 bool m_bProcessingEvents
;
197 sal_Int32 m_nLockCount
;
198 ::cppu::OInterfaceContainerHelper m_aUndoListeners
;
199 ::cppu::OInterfaceContainerHelper m_aModifyListeners
;
200 IUndoManagerImplementation
& m_rUndoManagerImplementation
;
201 ::std::stack
< bool > m_aContextVisibilities
;
202 #if OSL_DEBUG_LEVEL > 0
203 ::std::stack
< bool > m_aContextAPIFlags
;
205 ::std::queue
< ::rtl::Reference
< UndoManagerRequest
> >
209 ::osl::Mutex
& getMutex() { return m_aMutex
; }
212 UndoManagerHelper_Impl( IUndoManagerImplementation
& i_undoManagerImpl
)
216 ,m_bAPIActionRunning( false )
217 ,m_bProcessingEvents( false )
219 ,m_aUndoListeners( m_aMutex
)
220 ,m_aModifyListeners( m_aMutex
)
221 ,m_rUndoManagerImplementation( i_undoManagerImpl
)
223 getUndoManager().AddUndoListener( *this );
226 virtual ~UndoManagerHelper_Impl()
230 IUndoManager
& getUndoManager() const
232 return m_rUndoManagerImplementation
.getImplUndoManager();
235 Reference
< XUndoManager
> getXUndoManager() const
237 return m_rUndoManagerImplementation
.getThis();
241 virtual void actionUndone( const OUString
& i_actionComment
) SAL_OVERRIDE
;
242 virtual void actionRedone( const OUString
& i_actionComment
) SAL_OVERRIDE
;
243 virtual void undoActionAdded( const OUString
& i_actionComment
) SAL_OVERRIDE
;
244 virtual void cleared() SAL_OVERRIDE
;
245 virtual void clearedRedo() SAL_OVERRIDE
;
246 virtual void resetAll() SAL_OVERRIDE
;
247 virtual void listActionEntered( const OUString
& i_comment
) SAL_OVERRIDE
;
248 virtual void listActionLeft( const OUString
& i_comment
) SAL_OVERRIDE
;
249 virtual void listActionLeftAndMerged() SAL_OVERRIDE
;
250 virtual void listActionCancelled() SAL_OVERRIDE
;
251 virtual void undoManagerDying() SAL_OVERRIDE
;
256 void enterUndoContext( const OUString
& i_title
, const bool i_hidden
, IMutexGuard
& i_instanceLock
);
257 void leaveUndoContext( IMutexGuard
& i_instanceLock
);
258 void addUndoAction( const Reference
< XUndoAction
>& i_action
, IMutexGuard
& i_instanceLock
);
259 void undo( IMutexGuard
& i_instanceLock
);
260 void redo( IMutexGuard
& i_instanceLock
);
261 void clear( IMutexGuard
& i_instanceLock
);
262 void clearRedo( IMutexGuard
& i_instanceLock
);
263 void reset( IMutexGuard
& i_instanceLock
);
268 void addUndoManagerListener( const Reference
< XUndoManagerListener
>& i_listener
)
270 m_aUndoListeners
.addInterface( i_listener
);
273 void removeUndoManagerListener( const Reference
< XUndoManagerListener
>& i_listener
)
275 m_aUndoListeners
.removeInterface( i_listener
);
278 void addModifyListener( const Reference
< XModifyListener
>& i_listener
)
280 m_aModifyListeners
.addInterface( i_listener
);
283 void removeModifyListener( const Reference
< XModifyListener
>& i_listener
)
285 m_aModifyListeners
.removeInterface( i_listener
);
289 buildEvent( OUString
const& i_title
) const;
291 void impl_notifyModified();
292 void notify( OUString
const& i_title
,
293 void ( SAL_CALL
XUndoManagerListener::*i_notificationMethod
)( const UndoManagerEvent
& )
295 void notify( void ( SAL_CALL
XUndoManagerListener::*i_notificationMethod
)( const UndoManagerEvent
& ) )
297 notify( OUString(), i_notificationMethod
);
300 void notify( void ( SAL_CALL
XUndoManagerListener::*i_notificationMethod
)( const EventObject
& ) );
303 /// adds a function to be called to the request processor's queue
304 void impl_processRequest( ::boost::function0
< void > const& i_request
, IMutexGuard
& i_instanceLock
);
306 /// impl-versions of the XUndoManager API.
307 void impl_enterUndoContext( const OUString
& i_title
, const bool i_hidden
);
308 void impl_leaveUndoContext();
309 void impl_addUndoAction( const Reference
< XUndoAction
>& i_action
);
310 void impl_doUndoRedo( IMutexGuard
& i_externalLock
, const bool i_undo
);
312 void impl_clearRedo();
316 void UndoManagerHelper_Impl::disposing()
319 aEvent
.Source
= getXUndoManager();
320 m_aUndoListeners
.disposeAndClear( aEvent
);
321 m_aModifyListeners
.disposeAndClear( aEvent
);
323 ::osl::MutexGuard
aGuard( m_aMutex
);
325 getUndoManager().RemoveUndoListener( *this );
330 UndoManagerEvent
UndoManagerHelper_Impl::buildEvent( OUString
const& i_title
) const
332 UndoManagerEvent aEvent
;
333 aEvent
.Source
= getXUndoManager();
334 aEvent
.UndoActionTitle
= i_title
;
335 aEvent
.UndoContextDepth
= getUndoManager().GetListActionDepth();
339 void UndoManagerHelper_Impl::impl_notifyModified()
341 const EventObject
aEvent( getXUndoManager() );
342 m_aModifyListeners
.notifyEach( &XModifyListener::modified
, aEvent
);
345 void UndoManagerHelper_Impl::notify( OUString
const& i_title
,
346 void ( SAL_CALL
XUndoManagerListener::*i_notificationMethod
)( const UndoManagerEvent
& ) )
348 const UndoManagerEvent
aEvent( buildEvent( i_title
) );
350 // TODO: this notification method here is used by UndoManagerHelper_Impl, to multiplex the notifications we
351 // receive from the IUndoManager. Those notitications are sent with a locked SolarMutex, which means
352 // we're doing the multiplexing here with a locked SM, too. Which is Bad (TM).
353 // Fixing this properly would require outsourcing all the notifications into an own thread - which might lead
354 // to problems of its own, since clients might expect synchronous notifications.
356 m_aUndoListeners
.notifyEach( i_notificationMethod
, aEvent
);
357 impl_notifyModified();
360 void UndoManagerHelper_Impl::notify( void ( SAL_CALL
XUndoManagerListener::*i_notificationMethod
)( const EventObject
& ) )
362 const EventObject
aEvent( getXUndoManager() );
364 // TODO: the same comment as in the other notify, regarding SM locking applies here ...
366 m_aUndoListeners
.notifyEach( i_notificationMethod
, aEvent
);
367 impl_notifyModified();
370 void UndoManagerHelper_Impl::enterUndoContext( const OUString
& i_title
, const bool i_hidden
, IMutexGuard
& i_instanceLock
)
374 &UndoManagerHelper_Impl::impl_enterUndoContext
,
376 ::boost::cref( i_title
),
383 void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard
& i_instanceLock
)
387 &UndoManagerHelper_Impl::impl_leaveUndoContext
,
394 void UndoManagerHelper_Impl::addUndoAction( const Reference
< XUndoAction
>& i_action
, IMutexGuard
& i_instanceLock
)
396 if ( !i_action
.is() )
397 throw IllegalArgumentException(
398 "illegal undo action object",
405 &UndoManagerHelper_Impl::impl_addUndoAction
,
407 ::boost::ref( i_action
)
413 void UndoManagerHelper_Impl::clear( IMutexGuard
& i_instanceLock
)
417 &UndoManagerHelper_Impl::impl_clear
,
424 void UndoManagerHelper_Impl::clearRedo( IMutexGuard
& i_instanceLock
)
428 &UndoManagerHelper_Impl::impl_clearRedo
,
435 void UndoManagerHelper_Impl::reset( IMutexGuard
& i_instanceLock
)
439 &UndoManagerHelper_Impl::impl_reset
,
446 void UndoManagerHelper_Impl::lock()
449 ::osl::MutexGuard
aGuard( getMutex() );
451 if ( ++m_nLockCount
== 1 )
453 IUndoManager
& rUndoManager
= getUndoManager();
454 rUndoManager
.EnableUndo( false );
459 void UndoManagerHelper_Impl::unlock()
462 ::osl::MutexGuard
aGuard( getMutex() );
464 if ( m_nLockCount
== 0 )
465 throw NotLockedException( "Undo manager is not locked", getXUndoManager() );
467 if ( --m_nLockCount
== 0 )
469 IUndoManager
& rUndoManager
= getUndoManager();
470 rUndoManager
.EnableUndo( true );
475 void UndoManagerHelper_Impl::impl_processRequest( ::boost::function0
< void > const& i_request
, IMutexGuard
& i_instanceLock
)
477 // create the request, and add it to our queue
478 ::rtl::Reference
< UndoManagerRequest
> pRequest( new UndoManagerRequest( i_request
) );
480 ::osl::MutexGuard
aQueueGuard( m_aQueueMutex
);
481 m_aEventQueue
.push( pRequest
);
484 i_instanceLock
.clear();
486 if ( m_bProcessingEvents
)
488 // another thread is processing the event queue currently => it will also process the event which we just added
493 m_bProcessingEvents
= true;
498 ::osl::MutexGuard
aQueueGuard( m_aQueueMutex
);
499 if ( m_aEventQueue
.empty() )
501 // reset the flag before releasing the queue mutex, otherwise it's possible that another thread
502 // could add an event after we release the mutex, but before we reset the flag. If then this other
503 // thread checks the flag before be reset it, this thread's event would starve.
504 m_bProcessingEvents
= false;
507 pRequest
= m_aEventQueue
.front();
518 // no chance to process further requests, if the current one failed
520 ::osl::MutexGuard
aQueueGuard( m_aQueueMutex
);
521 while ( !m_aEventQueue
.empty() )
523 pRequest
= m_aEventQueue
.front();
525 pRequest
->cancel( getXUndoManager() );
527 m_bProcessingEvents
= false;
529 // re-throw the error
536 void UndoManagerHelper_Impl::impl_enterUndoContext( const OUString
& i_title
, const bool i_hidden
)
539 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
541 IUndoManager
& rUndoManager
= getUndoManager();
542 if ( !rUndoManager
.IsUndoEnabled() )
543 // ignore this request if the manager is locked
546 if ( i_hidden
&& ( rUndoManager
.GetUndoActionCount( IUndoManager::CurrentLevel
) == 0 ) )
547 throw EmptyUndoStackException(
548 "can't enter a hidden context without a previous Undo action",
549 m_rUndoManagerImplementation
.getThis()
553 ::comphelper::FlagGuard
aNotificationGuard( m_bAPIActionRunning
);
554 rUndoManager
.EnterListAction( i_title
, OUString() );
557 m_aContextVisibilities
.push( i_hidden
);
559 const UndoManagerEvent
aEvent( buildEvent( i_title
) );
563 m_aUndoListeners
.notifyEach( i_hidden
? &XUndoManagerListener::enteredHiddenContext
: &XUndoManagerListener::enteredContext
, aEvent
);
564 impl_notifyModified();
567 void UndoManagerHelper_Impl::impl_leaveUndoContext()
570 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
572 IUndoManager
& rUndoManager
= getUndoManager();
573 if ( !rUndoManager
.IsUndoEnabled() )
574 // ignore this request if the manager is locked
577 if ( !rUndoManager
.IsInListAction() )
578 throw InvalidStateException(
579 "no active undo context",
583 size_t nContextElements
= 0;
585 const bool isHiddenContext
= m_aContextVisibilities
.top();;
586 m_aContextVisibilities
.pop();
588 const bool bHadRedoActions
= ( rUndoManager
.GetRedoActionCount( IUndoManager::TopLevel
) > 0 );
590 ::comphelper::FlagGuard
aNotificationGuard( m_bAPIActionRunning
);
591 if ( isHiddenContext
)
592 nContextElements
= rUndoManager
.LeaveAndMergeListAction();
594 nContextElements
= rUndoManager
.LeaveListAction();
596 const bool bHasRedoActions
= ( rUndoManager
.GetRedoActionCount( IUndoManager::TopLevel
) > 0 );
598 // prepare notification
599 void ( SAL_CALL
XUndoManagerListener::*notificationMethod
)( const UndoManagerEvent
& ) = NULL
;
601 UndoManagerEvent
aContextEvent( buildEvent( OUString() ) );
602 const EventObject
aClearedEvent( getXUndoManager() );
603 if ( nContextElements
== 0 )
605 notificationMethod
= &XUndoManagerListener::cancelledContext
;
607 else if ( isHiddenContext
)
609 notificationMethod
= &XUndoManagerListener::leftHiddenContext
;
613 aContextEvent
.UndoActionTitle
= rUndoManager
.GetUndoActionComment( 0, IUndoManager::CurrentLevel
);
614 notificationMethod
= &XUndoManagerListener::leftContext
;
620 if ( bHadRedoActions
&& !bHasRedoActions
)
621 m_aUndoListeners
.notifyEach( &XUndoManagerListener::redoActionsCleared
, aClearedEvent
);
622 m_aUndoListeners
.notifyEach( notificationMethod
, aContextEvent
);
623 impl_notifyModified();
626 void UndoManagerHelper_Impl::impl_doUndoRedo( IMutexGuard
& i_externalLock
, const bool i_undo
)
628 ::osl::Guard
< ::framework::IMutex
> aExternalGuard( i_externalLock
.getGuardedMutex() );
629 // note that this assumes that the mutex has been released in the thread which added the
630 // Undo/Redo request, so we can successfully acquire it
633 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
635 IUndoManager
& rUndoManager
= getUndoManager();
636 if ( rUndoManager
.IsInListAction() )
637 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
639 const size_t nElements
= i_undo
640 ? rUndoManager
.GetUndoActionCount( IUndoManager::TopLevel
)
641 : rUndoManager
.GetRedoActionCount( IUndoManager::TopLevel
);
642 if ( nElements
== 0 )
643 throw EmptyUndoStackException("stack is empty", getXUndoManager() );
655 catch( const RuntimeException
& ) { /* allowed to leave here */ throw; }
656 catch( const UndoFailedException
& ) { /* allowed to leave here */ throw; }
657 catch( const Exception
& )
659 // not allowed to leave
660 const Any
aError( ::cppu::getCaughtException() );
661 throw UndoFailedException( OUString(), getXUndoManager(), aError
);
664 // note that in opposite to all of the other methods, we do *not* have our mutex locked when calling
665 // into the IUndoManager implementation. This ensures that an actual XUndoAction::undo/redo is also
666 // called without our mutex being locked.
667 // As a consequence, we do not set m_bAPIActionRunning here. Instead, our actionUndone/actionRedone methods
668 // *always* multiplex the event to our XUndoManagerListeners, not only when m_bAPIActionRunning is FALSE (This
669 // again is different from all other SfxUndoListener methods).
670 // So, we do not need to do this notification here ourself.
673 void UndoManagerHelper_Impl::impl_addUndoAction( const Reference
< XUndoAction
>& i_action
)
676 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
678 IUndoManager
& rUndoManager
= getUndoManager();
679 if ( !rUndoManager
.IsUndoEnabled() )
680 // ignore the request if the manager is locked
683 const UndoManagerEvent
aEventAdd( buildEvent( i_action
->getTitle() ) );
684 const EventObject
aEventClear( getXUndoManager() );
686 const bool bHadRedoActions
= ( rUndoManager
.GetRedoActionCount( IUndoManager::CurrentLevel
) > 0 );
688 ::comphelper::FlagGuard
aNotificationGuard( m_bAPIActionRunning
);
689 rUndoManager
.AddUndoAction( new UndoActionWrapper( i_action
) );
691 const bool bHasRedoActions
= ( rUndoManager
.GetRedoActionCount( IUndoManager::CurrentLevel
) > 0 );
696 m_aUndoListeners
.notifyEach( &XUndoManagerListener::undoActionAdded
, aEventAdd
);
697 if ( bHadRedoActions
&& !bHasRedoActions
)
698 m_aUndoListeners
.notifyEach( &XUndoManagerListener::redoActionsCleared
, aEventClear
);
699 impl_notifyModified();
702 void UndoManagerHelper_Impl::impl_clear()
705 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
707 IUndoManager
& rUndoManager
= getUndoManager();
708 if ( rUndoManager
.IsInListAction() )
709 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
712 ::comphelper::FlagGuard
aNotificationGuard( m_bAPIActionRunning
);
713 rUndoManager
.Clear();
716 const EventObject
aEvent( getXUndoManager() );
720 m_aUndoListeners
.notifyEach( &XUndoManagerListener::allActionsCleared
, aEvent
);
721 impl_notifyModified();
724 void UndoManagerHelper_Impl::impl_clearRedo()
727 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
729 IUndoManager
& rUndoManager
= getUndoManager();
730 if ( rUndoManager
.IsInListAction() )
731 throw UndoContextNotClosedException( OUString(), getXUndoManager() );
734 ::comphelper::FlagGuard
aNotificationGuard( m_bAPIActionRunning
);
735 rUndoManager
.ClearRedo();
738 const EventObject
aEvent( getXUndoManager() );
742 m_aUndoListeners
.notifyEach( &XUndoManagerListener::redoActionsCleared
, aEvent
);
743 impl_notifyModified();
746 void UndoManagerHelper_Impl::impl_reset()
749 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
751 IUndoManager
& rUndoManager
= getUndoManager();
753 ::comphelper::FlagGuard
aNotificationGuard( m_bAPIActionRunning
);
754 rUndoManager
.Reset();
757 const EventObject
aEvent( getXUndoManager() );
761 m_aUndoListeners
.notifyEach( &XUndoManagerListener::resetAll
, aEvent
);
762 impl_notifyModified();
765 void UndoManagerHelper_Impl::actionUndone( const OUString
& i_actionComment
)
767 UndoManagerEvent aEvent
;
768 aEvent
.Source
= getXUndoManager();
769 aEvent
.UndoActionTitle
= i_actionComment
;
770 aEvent
.UndoContextDepth
= 0; // Undo can happen on level 0 only
771 m_aUndoListeners
.notifyEach( &XUndoManagerListener::actionUndone
, aEvent
);
772 impl_notifyModified();
775 void UndoManagerHelper_Impl::actionRedone( const OUString
& i_actionComment
)
777 UndoManagerEvent aEvent
;
778 aEvent
.Source
= getXUndoManager();
779 aEvent
.UndoActionTitle
= i_actionComment
;
780 aEvent
.UndoContextDepth
= 0; // Redo can happen on level 0 only
781 m_aUndoListeners
.notifyEach( &XUndoManagerListener::actionRedone
, aEvent
);
782 impl_notifyModified();
785 void UndoManagerHelper_Impl::undoActionAdded( const OUString
& i_actionComment
)
787 if ( m_bAPIActionRunning
)
790 notify( i_actionComment
, &XUndoManagerListener::undoActionAdded
);
793 void UndoManagerHelper_Impl::cleared()
795 if ( m_bAPIActionRunning
)
798 notify( &XUndoManagerListener::allActionsCleared
);
801 void UndoManagerHelper_Impl::clearedRedo()
803 if ( m_bAPIActionRunning
)
806 notify( &XUndoManagerListener::redoActionsCleared
);
809 void UndoManagerHelper_Impl::resetAll()
811 if ( m_bAPIActionRunning
)
814 notify( &XUndoManagerListener::resetAll
);
817 void UndoManagerHelper_Impl::listActionEntered( const OUString
& i_comment
)
819 #if OSL_DEBUG_LEVEL > 0
820 m_aContextAPIFlags
.push( m_bAPIActionRunning
);
823 if ( m_bAPIActionRunning
)
826 notify( i_comment
, &XUndoManagerListener::enteredContext
);
829 void UndoManagerHelper_Impl::listActionLeft( const OUString
& i_comment
)
831 #if OSL_DEBUG_LEVEL > 0
832 const bool bCurrentContextIsAPIContext
= m_aContextAPIFlags
.top();
833 m_aContextAPIFlags
.pop();
834 OSL_ENSURE( bCurrentContextIsAPIContext
== m_bAPIActionRunning
, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" );
837 if ( m_bAPIActionRunning
)
840 notify( i_comment
, &XUndoManagerListener::leftContext
);
843 void UndoManagerHelper_Impl::listActionLeftAndMerged()
845 #if OSL_DEBUG_LEVEL > 0
846 const bool bCurrentContextIsAPIContext
= m_aContextAPIFlags
.top();
847 m_aContextAPIFlags
.pop();
848 OSL_ENSURE( bCurrentContextIsAPIContext
== m_bAPIActionRunning
, "UndoManagerHelper_Impl::listActionLeftAndMerged: API and non-API contexts interwoven!" );
851 if ( m_bAPIActionRunning
)
854 notify( &XUndoManagerListener::leftHiddenContext
);
857 void UndoManagerHelper_Impl::listActionCancelled()
859 #if OSL_DEBUG_LEVEL > 0
860 const bool bCurrentContextIsAPIContext
= m_aContextAPIFlags
.top();
861 m_aContextAPIFlags
.pop();
862 OSL_ENSURE( bCurrentContextIsAPIContext
== m_bAPIActionRunning
, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" );
865 if ( m_bAPIActionRunning
)
868 notify( &XUndoManagerListener::cancelledContext
);
871 void UndoManagerHelper_Impl::undoManagerDying()
873 // TODO: do we need to care? Or is this the responsibility of our owner?
876 //= UndoManagerHelper
878 UndoManagerHelper::UndoManagerHelper( IUndoManagerImplementation
& i_undoManagerImpl
)
879 :m_xImpl( new UndoManagerHelper_Impl( i_undoManagerImpl
) )
883 UndoManagerHelper::~UndoManagerHelper()
887 void UndoManagerHelper::disposing()
889 m_xImpl
->disposing();
892 void UndoManagerHelper::enterUndoContext( const OUString
& i_title
, IMutexGuard
& i_instanceLock
)
894 m_xImpl
->enterUndoContext( i_title
, false, i_instanceLock
);
897 void UndoManagerHelper::enterHiddenUndoContext( IMutexGuard
& i_instanceLock
)
899 m_xImpl
->enterUndoContext( OUString(), true, i_instanceLock
);
902 void UndoManagerHelper::leaveUndoContext( IMutexGuard
& i_instanceLock
)
904 m_xImpl
->leaveUndoContext( i_instanceLock
);
907 void UndoManagerHelper_Impl::undo( IMutexGuard
& i_instanceLock
)
911 &UndoManagerHelper_Impl::impl_doUndoRedo
,
913 ::boost::ref( i_instanceLock
),
920 void UndoManagerHelper_Impl::redo( IMutexGuard
& i_instanceLock
)
924 &UndoManagerHelper_Impl::impl_doUndoRedo
,
926 ::boost::ref( i_instanceLock
),
933 void UndoManagerHelper::addUndoAction( const Reference
< XUndoAction
>& i_action
, IMutexGuard
& i_instanceLock
)
935 m_xImpl
->addUndoAction( i_action
, i_instanceLock
);
938 void UndoManagerHelper::undo( IMutexGuard
& i_instanceLock
)
940 m_xImpl
->undo( i_instanceLock
);
943 void UndoManagerHelper::redo( IMutexGuard
& i_instanceLock
)
945 m_xImpl
->redo( i_instanceLock
);
948 bool UndoManagerHelper::isUndoPossible() const
951 ::osl::MutexGuard
aGuard( m_xImpl
->getMutex() );
952 IUndoManager
& rUndoManager
= m_xImpl
->getUndoManager();
953 if ( rUndoManager
.IsInListAction() )
955 return rUndoManager
.GetUndoActionCount( IUndoManager::TopLevel
) > 0;
959 bool UndoManagerHelper::isRedoPossible() const
962 ::osl::MutexGuard
aGuard( m_xImpl
->getMutex() );
963 const IUndoManager
& rUndoManager
= m_xImpl
->getUndoManager();
964 if ( rUndoManager
.IsInListAction() )
966 return rUndoManager
.GetRedoActionCount( IUndoManager::TopLevel
) > 0;
973 OUString
lcl_getCurrentActionTitle( UndoManagerHelper_Impl
& i_impl
, const bool i_undo
)
976 ::osl::MutexGuard
aGuard( i_impl
.getMutex() );
978 const IUndoManager
& rUndoManager
= i_impl
.getUndoManager();
979 const size_t nActionCount
= i_undo
980 ? rUndoManager
.GetUndoActionCount( IUndoManager::TopLevel
)
981 : rUndoManager
.GetRedoActionCount( IUndoManager::TopLevel
);
982 if ( nActionCount
== 0 )
983 throw EmptyUndoStackException(
984 i_undo
? OUString( "no action on the undo stack" )
985 : OUString( "no action on the redo stack" ),
986 i_impl
.getXUndoManager()
989 ? rUndoManager
.GetUndoActionComment( 0, IUndoManager::TopLevel
)
990 : rUndoManager
.GetRedoActionComment( 0, IUndoManager::TopLevel
);
994 Sequence
< OUString
> lcl_getAllActionTitles( UndoManagerHelper_Impl
& i_impl
, const bool i_undo
)
997 ::osl::MutexGuard
aGuard( i_impl
.getMutex() );
999 const IUndoManager
& rUndoManager
= i_impl
.getUndoManager();
1000 const size_t nCount
= i_undo
1001 ? rUndoManager
.GetUndoActionCount( IUndoManager::TopLevel
)
1002 : rUndoManager
.GetRedoActionCount( IUndoManager::TopLevel
);
1004 Sequence
< OUString
> aTitles( nCount
);
1005 for ( size_t i
=0; i
<nCount
; ++i
)
1008 ? rUndoManager
.GetUndoActionComment( i
, IUndoManager::TopLevel
)
1009 : rUndoManager
.GetRedoActionComment( i
, IUndoManager::TopLevel
);
1012 // <--- SYNCHRONIZED
1016 OUString
UndoManagerHelper::getCurrentUndoActionTitle() const
1018 return lcl_getCurrentActionTitle( *m_xImpl
, true );
1021 OUString
UndoManagerHelper::getCurrentRedoActionTitle() const
1023 return lcl_getCurrentActionTitle( *m_xImpl
, false );
1026 Sequence
< OUString
> UndoManagerHelper::getAllUndoActionTitles() const
1028 return lcl_getAllActionTitles( *m_xImpl
, true );
1031 Sequence
< OUString
> UndoManagerHelper::getAllRedoActionTitles() const
1033 return lcl_getAllActionTitles( *m_xImpl
, false );
1036 void UndoManagerHelper::clear( IMutexGuard
& i_instanceLock
)
1038 m_xImpl
->clear( i_instanceLock
);
1041 void UndoManagerHelper::clearRedo( IMutexGuard
& i_instanceLock
)
1043 m_xImpl
->clearRedo( i_instanceLock
);
1046 void UndoManagerHelper::reset( IMutexGuard
& i_instanceLock
)
1048 m_xImpl
->reset( i_instanceLock
);
1051 void UndoManagerHelper::lock()
1056 void UndoManagerHelper::unlock()
1061 bool UndoManagerHelper::isLocked()
1063 // SYNCHRONIZED --->
1064 ::osl::MutexGuard
aGuard( m_xImpl
->getMutex() );
1066 IUndoManager
& rUndoManager
= m_xImpl
->getUndoManager();
1067 return !rUndoManager
.IsUndoEnabled();
1068 // <--- SYNCHRONIZED
1071 void UndoManagerHelper::addUndoManagerListener( const Reference
< XUndoManagerListener
>& i_listener
)
1073 if ( i_listener
.is() )
1074 m_xImpl
->addUndoManagerListener( i_listener
);
1077 void UndoManagerHelper::removeUndoManagerListener( const Reference
< XUndoManagerListener
>& i_listener
)
1079 if ( i_listener
.is() )
1080 m_xImpl
->removeUndoManagerListener( i_listener
);
1083 void UndoManagerHelper::addModifyListener( const Reference
< XModifyListener
>& i_listener
)
1085 if ( i_listener
.is() )
1086 m_xImpl
->addModifyListener( i_listener
);
1089 void UndoManagerHelper::removeModifyListener( const Reference
< XModifyListener
>& i_listener
)
1091 if ( i_listener
.is() )
1092 m_xImpl
->removeModifyListener( i_listener
);
1095 } // namespace framework
1097 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */