fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / framework / source / fwe / helper / undomanagerhelper.cxx
blob8b0c9c58593d2d7803556fc24bff6912143769ad
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>
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>
32 #include <stack>
33 #include <queue>
34 #include <boost/function.hpp>
36 namespace framework
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;
65 //= UndoActionWrapper
67 class UndoActionWrapper : public SfxUndoAction
69 public:
70 UndoActionWrapper(
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;
80 private:
81 const Reference< XUndoAction > m_xUndoAction;
84 UndoActionWrapper::UndoActionWrapper( Reference< XUndoAction > const& i_undoAction )
85 :SfxUndoAction()
86 ,m_xUndoAction( i_undoAction )
88 ENSURE_OR_THROW( m_xUndoAction.is(), "illegal undo action" );
91 UndoActionWrapper::~UndoActionWrapper()
93 try
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
107 OUString sComment;
110 sComment = m_xUndoAction->getTitle();
112 catch( const Exception& )
114 DBG_UNHANDLED_EXCEPTION();
116 return sComment;
119 void UndoActionWrapper::Undo()
121 m_xUndoAction->undo();
124 void UndoActionWrapper::Redo()
126 m_xUndoAction->redo();
129 bool UndoActionWrapper::CanRepeat(SfxRepeatTarget&) const
131 return false;
134 //= UndoManagerRequest
136 class UndoManagerRequest : public ::comphelper::AnyEvent
138 public:
139 UndoManagerRequest( ::boost::function0< void > const& i_request )
140 :m_request( i_request )
141 ,m_caughtException()
142 ,m_finishCondition()
144 m_finishCondition.reset();
147 void execute()
151 m_request();
153 catch( const Exception& )
155 m_caughtException = ::cppu::getCaughtException();
157 m_finishCondition.set();
160 void wait()
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." ),
171 i_context
173 m_finishCondition.set();
176 protected:
177 virtual ~UndoManagerRequest()
181 private:
182 ::boost::function0< void > m_request;
183 Any m_caughtException;
184 ::osl::Condition m_finishCondition;
187 //= UndoManagerHelper_Impl
189 class UndoManagerHelper_Impl : public SfxUndoListener
191 private:
192 ::osl::Mutex m_aMutex;
193 ::osl::Mutex m_aQueueMutex;
194 bool m_disposed;
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;
204 #endif
205 ::std::queue< ::rtl::Reference< UndoManagerRequest > >
206 m_aEventQueue;
208 public:
209 ::osl::Mutex& getMutex() { return m_aMutex; }
211 public:
212 UndoManagerHelper_Impl( IUndoManagerImplementation& i_undoManagerImpl )
213 :m_aMutex()
214 ,m_aQueueMutex()
215 ,m_disposed( false )
216 ,m_bAPIActionRunning( false )
217 ,m_bProcessingEvents( false )
218 ,m_nLockCount( 0 )
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();
240 // SfxUndoListener
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;
253 // public operations
254 void disposing();
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 );
265 void lock();
266 void unlock();
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 );
288 UndoManagerEvent
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& ) );
302 private:
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 );
311 void impl_clear();
312 void impl_clearRedo();
313 void impl_reset();
316 void UndoManagerHelper_Impl::disposing()
318 EventObject aEvent;
319 aEvent.Source = getXUndoManager();
320 m_aUndoListeners.disposeAndClear( aEvent );
321 m_aModifyListeners.disposeAndClear( aEvent );
323 ::osl::MutexGuard aGuard( m_aMutex );
325 getUndoManager().RemoveUndoListener( *this );
327 m_disposed = true;
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();
336 return aEvent;
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 )
372 impl_processRequest(
373 ::boost::bind(
374 &UndoManagerHelper_Impl::impl_enterUndoContext,
375 this,
376 ::boost::cref( i_title ),
377 i_hidden
379 i_instanceLock
383 void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard& i_instanceLock )
385 impl_processRequest(
386 ::boost::bind(
387 &UndoManagerHelper_Impl::impl_leaveUndoContext,
388 this
390 i_instanceLock
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",
399 getXUndoManager(),
403 impl_processRequest(
404 ::boost::bind(
405 &UndoManagerHelper_Impl::impl_addUndoAction,
406 this,
407 ::boost::ref( i_action )
409 i_instanceLock
413 void UndoManagerHelper_Impl::clear( IMutexGuard& i_instanceLock )
415 impl_processRequest(
416 ::boost::bind(
417 &UndoManagerHelper_Impl::impl_clear,
418 this
420 i_instanceLock
424 void UndoManagerHelper_Impl::clearRedo( IMutexGuard& i_instanceLock )
426 impl_processRequest(
427 ::boost::bind(
428 &UndoManagerHelper_Impl::impl_clearRedo,
429 this
431 i_instanceLock
435 void UndoManagerHelper_Impl::reset( IMutexGuard& i_instanceLock )
437 impl_processRequest(
438 ::boost::bind(
439 &UndoManagerHelper_Impl::impl_reset,
440 this
442 i_instanceLock
446 void UndoManagerHelper_Impl::lock()
448 // SYNCHRONIZED --->
449 ::osl::MutexGuard aGuard( getMutex() );
451 if ( ++m_nLockCount == 1 )
453 IUndoManager& rUndoManager = getUndoManager();
454 rUndoManager.EnableUndo( false );
456 // <--- SYNCHRONIZED
459 void UndoManagerHelper_Impl::unlock()
461 // SYNCHRONIZED --->
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 );
472 // <--- SYNCHRONIZED
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
489 pRequest->wait();
490 return;
493 m_bProcessingEvents = true;
496 pRequest.clear();
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;
505 return;
507 pRequest = m_aEventQueue.front();
508 m_aEventQueue.pop();
512 pRequest->execute();
513 pRequest->wait();
515 catch( ... )
518 // no chance to process further requests, if the current one failed
519 // => discard them
520 ::osl::MutexGuard aQueueGuard( m_aQueueMutex );
521 while ( !m_aEventQueue.empty() )
523 pRequest = m_aEventQueue.front();
524 m_aEventQueue.pop();
525 pRequest->cancel( getXUndoManager() );
527 m_bProcessingEvents = false;
529 // re-throw the error
530 throw;
533 while ( true );
536 void UndoManagerHelper_Impl::impl_enterUndoContext( const OUString& i_title, const bool i_hidden )
538 // SYNCHRONIZED --->
539 ::osl::ClearableMutexGuard aGuard( m_aMutex );
541 IUndoManager& rUndoManager = getUndoManager();
542 if ( !rUndoManager.IsUndoEnabled() )
543 // ignore this request if the manager is locked
544 return;
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 ) );
560 aGuard.clear();
561 // <--- SYNCHRONIZED
563 m_aUndoListeners.notifyEach( i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, aEvent );
564 impl_notifyModified();
567 void UndoManagerHelper_Impl::impl_leaveUndoContext()
569 // SYNCHRONIZED --->
570 ::osl::ClearableMutexGuard aGuard( m_aMutex );
572 IUndoManager& rUndoManager = getUndoManager();
573 if ( !rUndoManager.IsUndoEnabled() )
574 // ignore this request if the manager is locked
575 return;
577 if ( !rUndoManager.IsInListAction() )
578 throw InvalidStateException(
579 "no active undo context",
580 getXUndoManager()
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();
593 else
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;
611 else
613 aContextEvent.UndoActionTitle = rUndoManager.GetUndoActionComment( 0, IUndoManager::CurrentLevel );
614 notificationMethod = &XUndoManagerListener::leftContext;
617 aGuard.clear();
618 // <--- SYNCHRONIZED
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
632 // SYNCHRONIZED --->
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() );
645 aGuard.clear();
646 // <--- SYNCHRONIZED
650 if ( i_undo )
651 rUndoManager.Undo();
652 else
653 rUndoManager.Redo();
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 )
675 // SYNCHRONIZED --->
676 ::osl::ClearableMutexGuard aGuard( m_aMutex );
678 IUndoManager& rUndoManager = getUndoManager();
679 if ( !rUndoManager.IsUndoEnabled() )
680 // ignore the request if the manager is locked
681 return;
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 );
693 aGuard.clear();
694 // <--- SYNCHRONIZED
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()
704 // SYNCHRONIZED --->
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() );
717 aGuard.clear();
718 // <--- SYNCHRONIZED
720 m_aUndoListeners.notifyEach( &XUndoManagerListener::allActionsCleared, aEvent );
721 impl_notifyModified();
724 void UndoManagerHelper_Impl::impl_clearRedo()
726 // SYNCHRONIZED --->
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() );
739 aGuard.clear();
740 // <--- SYNCHRONIZED
742 m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEvent );
743 impl_notifyModified();
746 void UndoManagerHelper_Impl::impl_reset()
748 // SYNCHRONIZED --->
749 ::osl::ClearableMutexGuard aGuard( m_aMutex );
751 IUndoManager& rUndoManager = getUndoManager();
753 ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
754 rUndoManager.Reset();
757 const EventObject aEvent( getXUndoManager() );
758 aGuard.clear();
759 // <--- SYNCHRONIZED
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 )
788 return;
790 notify( i_actionComment, &XUndoManagerListener::undoActionAdded );
793 void UndoManagerHelper_Impl::cleared()
795 if ( m_bAPIActionRunning )
796 return;
798 notify( &XUndoManagerListener::allActionsCleared );
801 void UndoManagerHelper_Impl::clearedRedo()
803 if ( m_bAPIActionRunning )
804 return;
806 notify( &XUndoManagerListener::redoActionsCleared );
809 void UndoManagerHelper_Impl::resetAll()
811 if ( m_bAPIActionRunning )
812 return;
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 );
821 #endif
823 if ( m_bAPIActionRunning )
824 return;
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!" );
835 #endif
837 if ( m_bAPIActionRunning )
838 return;
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!" );
849 #endif
851 if ( m_bAPIActionRunning )
852 return;
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!" );
863 #endif
865 if ( m_bAPIActionRunning )
866 return;
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 )
909 impl_processRequest(
910 ::boost::bind(
911 &UndoManagerHelper_Impl::impl_doUndoRedo,
912 this,
913 ::boost::ref( i_instanceLock ),
914 true
916 i_instanceLock
920 void UndoManagerHelper_Impl::redo( IMutexGuard& i_instanceLock )
922 impl_processRequest(
923 ::boost::bind(
924 &UndoManagerHelper_Impl::impl_doUndoRedo,
925 this,
926 ::boost::ref( i_instanceLock ),
927 false
929 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
950 // SYNCHRONIZED --->
951 ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
952 IUndoManager& rUndoManager = m_xImpl->getUndoManager();
953 if ( rUndoManager.IsInListAction() )
954 return false;
955 return rUndoManager.GetUndoActionCount( IUndoManager::TopLevel ) > 0;
956 // <--- SYNCHRONIZED
959 bool UndoManagerHelper::isRedoPossible() const
961 // SYNCHRONIZED --->
962 ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
963 const IUndoManager& rUndoManager = m_xImpl->getUndoManager();
964 if ( rUndoManager.IsInListAction() )
965 return false;
966 return rUndoManager.GetRedoActionCount( IUndoManager::TopLevel ) > 0;
967 // <--- SYNCHRONIZED
970 namespace
973 OUString lcl_getCurrentActionTitle( UndoManagerHelper_Impl& i_impl, const bool i_undo )
975 // SYNCHRONIZED --->
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()
988 return i_undo
989 ? rUndoManager.GetUndoActionComment( 0, IUndoManager::TopLevel )
990 : rUndoManager.GetRedoActionComment( 0, IUndoManager::TopLevel );
991 // <--- SYNCHRONIZED
994 Sequence< OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl, const bool i_undo )
996 // SYNCHRONIZED --->
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 )
1007 aTitles[i] = i_undo
1008 ? rUndoManager.GetUndoActionComment( i, IUndoManager::TopLevel )
1009 : rUndoManager.GetRedoActionComment( i, IUndoManager::TopLevel );
1011 return aTitles;
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()
1053 m_xImpl->lock();
1056 void UndoManagerHelper::unlock()
1058 m_xImpl->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: */