Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : #include "framework/undomanagerhelper.hxx"
22 :
23 : #include <com/sun/star/lang/XComponent.hpp>
24 :
25 : #include <cppuhelper/interfacecontainer.hxx>
26 : #include <cppuhelper/exc_hlp.hxx>
27 : #include <comphelper/flagguard.hxx>
28 : #include <comphelper/asyncnotification.hxx>
29 : #include <svl/undo.hxx>
30 : #include <tools/diagnose_ex.h>
31 : #include <osl/conditn.hxx>
32 :
33 : #include <stack>
34 : #include <queue>
35 : #include <boost/function.hpp>
36 :
37 : //......................................................................................................................
38 : namespace framework
39 : {
40 : //......................................................................................................................
41 :
42 : /** === begin UNO using === **/
43 : using ::com::sun::star::uno::Reference;
44 : using ::com::sun::star::uno::XInterface;
45 : using ::com::sun::star::uno::UNO_QUERY;
46 : using ::com::sun::star::uno::UNO_QUERY_THROW;
47 : using ::com::sun::star::uno::UNO_SET_THROW;
48 : using ::com::sun::star::uno::Exception;
49 : using ::com::sun::star::uno::RuntimeException;
50 : using ::com::sun::star::uno::Any;
51 : using ::com::sun::star::uno::makeAny;
52 : using ::com::sun::star::uno::Sequence;
53 : using ::com::sun::star::uno::Type;
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;
67 : /** === end UNO using === **/
68 : using ::svl::IUndoManager;
69 :
70 : //==================================================================================================================
71 : //= UndoActionWrapper
72 : //==================================================================================================================
73 : class UndoActionWrapper : public SfxUndoAction
74 : {
75 : public:
76 : UndoActionWrapper(
77 : Reference< XUndoAction > const& i_undoAction
78 : );
79 : virtual ~UndoActionWrapper();
80 :
81 : virtual rtl::OUString GetComment() const;
82 : virtual void Undo();
83 : virtual void Redo();
84 : virtual sal_Bool CanRepeat(SfxRepeatTarget&) const;
85 :
86 : private:
87 : const Reference< XUndoAction > m_xUndoAction;
88 : };
89 :
90 : //------------------------------------------------------------------------------------------------------------------
91 0 : UndoActionWrapper::UndoActionWrapper( Reference< XUndoAction > const& i_undoAction )
92 : :SfxUndoAction()
93 0 : ,m_xUndoAction( i_undoAction )
94 : {
95 0 : ENSURE_OR_THROW( m_xUndoAction.is(), "illegal undo action" );
96 0 : }
97 :
98 : //------------------------------------------------------------------------------------------------------------------
99 0 : UndoActionWrapper::~UndoActionWrapper()
100 : {
101 : try
102 : {
103 0 : Reference< XComponent > xComponent( m_xUndoAction, UNO_QUERY );
104 0 : if ( xComponent.is() )
105 0 : xComponent->dispose();
106 : }
107 0 : catch( const Exception& )
108 : {
109 : DBG_UNHANDLED_EXCEPTION();
110 : }
111 0 : }
112 :
113 : //------------------------------------------------------------------------------------------------------------------
114 0 : rtl::OUString UndoActionWrapper::GetComment() const
115 : {
116 0 : rtl::OUString sComment;
117 : try
118 : {
119 0 : sComment = m_xUndoAction->getTitle();
120 : }
121 0 : catch( const Exception& )
122 : {
123 : DBG_UNHANDLED_EXCEPTION();
124 : }
125 0 : return sComment;
126 : }
127 :
128 : //------------------------------------------------------------------------------------------------------------------
129 0 : void UndoActionWrapper::Undo()
130 : {
131 0 : m_xUndoAction->undo();
132 0 : }
133 :
134 : //------------------------------------------------------------------------------------------------------------------
135 0 : void UndoActionWrapper::Redo()
136 : {
137 0 : m_xUndoAction->redo();
138 0 : }
139 :
140 : //------------------------------------------------------------------------------------------------------------------
141 0 : sal_Bool UndoActionWrapper::CanRepeat(SfxRepeatTarget&) const
142 : {
143 0 : return sal_False;
144 : }
145 :
146 : //==================================================================================================================
147 : //= UndoManagerRequest
148 : //==================================================================================================================
149 : class UndoManagerRequest : public ::comphelper::AnyEvent
150 : {
151 : public:
152 0 : UndoManagerRequest( ::boost::function0< void > const& i_request )
153 : :m_request( i_request )
154 : ,m_caughtException()
155 0 : ,m_finishCondition()
156 : {
157 0 : m_finishCondition.reset();
158 0 : }
159 :
160 0 : void execute()
161 : {
162 : try
163 : {
164 0 : m_request();
165 : }
166 0 : catch( const Exception& )
167 : {
168 0 : m_caughtException = ::cppu::getCaughtException();
169 : }
170 0 : m_finishCondition.set();
171 0 : }
172 :
173 0 : void wait()
174 : {
175 0 : m_finishCondition.wait();
176 0 : if ( m_caughtException.hasValue() )
177 0 : ::cppu::throwException( m_caughtException );
178 0 : }
179 :
180 0 : void cancel( const Reference< XInterface >& i_context )
181 : {
182 : m_caughtException <<= RuntimeException(
183 : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Concurrency error: an ealier operation on the stack failed." ) ),
184 : i_context
185 0 : );
186 0 : m_finishCondition.set();
187 0 : }
188 :
189 : protected:
190 0 : ~UndoManagerRequest()
191 0 : {
192 0 : }
193 :
194 : private:
195 : ::boost::function0< void > m_request;
196 : Any m_caughtException;
197 : ::osl::Condition m_finishCondition;
198 : };
199 :
200 : //------------------------------------------------------------------------------------------------------------------
201 :
202 : //==================================================================================================================
203 : //= UndoManagerHelper_Impl
204 : //==================================================================================================================
205 : class UndoManagerHelper_Impl : public SfxUndoListener
206 : {
207 : private:
208 : ::osl::Mutex m_aMutex;
209 : ::osl::Mutex m_aQueueMutex;
210 : bool m_disposed;
211 : bool m_bAPIActionRunning;
212 : bool m_bProcessingEvents;
213 : sal_Int32 m_nLockCount;
214 : ::cppu::OInterfaceContainerHelper m_aUndoListeners;
215 : ::cppu::OInterfaceContainerHelper m_aModifyListeners;
216 : IUndoManagerImplementation& m_rUndoManagerImplementation;
217 : ::std::stack< bool > m_aContextVisibilities;
218 : #if OSL_DEBUG_LEVEL > 0
219 : ::std::stack< bool > m_aContextAPIFlags;
220 : #endif
221 : ::std::queue< ::rtl::Reference< UndoManagerRequest > >
222 : m_aEventQueue;
223 :
224 : public:
225 0 : ::osl::Mutex& getMutex() { return m_aMutex; }
226 :
227 : public:
228 1 : UndoManagerHelper_Impl( IUndoManagerImplementation& i_undoManagerImpl )
229 : :m_aMutex()
230 : ,m_aQueueMutex()
231 : ,m_disposed( false )
232 : ,m_bAPIActionRunning( false )
233 : ,m_bProcessingEvents( false )
234 : ,m_nLockCount( 0 )
235 : ,m_aUndoListeners( m_aMutex )
236 : ,m_aModifyListeners( m_aMutex )
237 1 : ,m_rUndoManagerImplementation( i_undoManagerImpl )
238 : {
239 1 : getUndoManager().AddUndoListener( *this );
240 1 : }
241 :
242 0 : virtual ~UndoManagerHelper_Impl()
243 0 : {
244 0 : }
245 :
246 : //..............................................................................................................
247 2 : IUndoManager& getUndoManager() const
248 : {
249 2 : return m_rUndoManagerImplementation.getImplUndoManager();
250 : }
251 :
252 : //..............................................................................................................
253 1 : Reference< XUndoManager > getXUndoManager() const
254 : {
255 1 : return m_rUndoManagerImplementation.getThis();
256 : }
257 :
258 : // SfxUndoListener
259 : virtual void actionUndone( const String& i_actionComment );
260 : virtual void actionRedone( const String& i_actionComment );
261 : virtual void undoActionAdded( const String& i_actionComment );
262 : virtual void cleared();
263 : virtual void clearedRedo();
264 : virtual void resetAll();
265 : virtual void listActionEntered( const String& i_comment );
266 : virtual void listActionLeft( const String& i_comment );
267 : virtual void listActionLeftAndMerged();
268 : virtual void listActionCancelled();
269 : virtual void undoManagerDying();
270 :
271 : // public operations
272 : void disposing();
273 :
274 : void enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock );
275 : void leaveUndoContext( IMutexGuard& i_instanceLock );
276 : void addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock );
277 : void undo( IMutexGuard& i_instanceLock );
278 : void redo( IMutexGuard& i_instanceLock );
279 : void clear( IMutexGuard& i_instanceLock );
280 : void clearRedo( IMutexGuard& i_instanceLock );
281 : void reset( IMutexGuard& i_instanceLock );
282 :
283 : void lock();
284 : void unlock();
285 :
286 1 : void addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
287 : {
288 1 : m_aUndoListeners.addInterface( i_listener );
289 1 : }
290 :
291 1 : void removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
292 : {
293 1 : m_aUndoListeners.removeInterface( i_listener );
294 1 : }
295 :
296 0 : void addModifyListener( const Reference< XModifyListener >& i_listener )
297 : {
298 0 : m_aModifyListeners.addInterface( i_listener );
299 0 : }
300 :
301 0 : void removeModifyListener( const Reference< XModifyListener >& i_listener )
302 : {
303 0 : m_aModifyListeners.removeInterface( i_listener );
304 0 : }
305 :
306 : UndoManagerEvent
307 : buildEvent( ::rtl::OUString const& i_title ) const;
308 :
309 : void impl_notifyModified();
310 : void notify( ::rtl::OUString const& i_title,
311 : void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& )
312 : );
313 0 : void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) )
314 : {
315 0 : notify( ::rtl::OUString(), i_notificationMethod );
316 0 : }
317 :
318 : void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) );
319 :
320 : private:
321 : /// adds a function to be called to the request processor's queue
322 : void impl_processRequest( ::boost::function0< void > const& i_request, IMutexGuard& i_instanceLock );
323 :
324 : /// impl-versions of the XUndoManager API.
325 : void impl_enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden );
326 : void impl_leaveUndoContext();
327 : void impl_addUndoAction( const Reference< XUndoAction >& i_action );
328 : void impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo );
329 : void impl_clear();
330 : void impl_clearRedo();
331 : void impl_reset();
332 : };
333 :
334 : //------------------------------------------------------------------------------------------------------------------
335 1 : void UndoManagerHelper_Impl::disposing()
336 : {
337 1 : EventObject aEvent;
338 1 : aEvent.Source = getXUndoManager();
339 1 : m_aUndoListeners.disposeAndClear( aEvent );
340 1 : m_aModifyListeners.disposeAndClear( aEvent );
341 :
342 1 : ::osl::MutexGuard aGuard( m_aMutex );
343 :
344 1 : getUndoManager().RemoveUndoListener( *this );
345 :
346 1 : m_disposed = true;
347 1 : }
348 :
349 : //------------------------------------------------------------------------------------------------------------------
350 0 : UndoManagerEvent UndoManagerHelper_Impl::buildEvent( ::rtl::OUString const& i_title ) const
351 : {
352 0 : UndoManagerEvent aEvent;
353 0 : aEvent.Source = getXUndoManager();
354 0 : aEvent.UndoActionTitle = i_title;
355 0 : aEvent.UndoContextDepth = getUndoManager().GetListActionDepth();
356 0 : return aEvent;
357 : }
358 :
359 : //------------------------------------------------------------------------------------------------------------------
360 0 : void UndoManagerHelper_Impl::impl_notifyModified()
361 : {
362 0 : const EventObject aEvent( getXUndoManager() );
363 0 : m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent );
364 0 : }
365 :
366 : //------------------------------------------------------------------------------------------------------------------
367 0 : void UndoManagerHelper_Impl::notify( ::rtl::OUString const& i_title,
368 : void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) )
369 : {
370 0 : const UndoManagerEvent aEvent( buildEvent( i_title ) );
371 :
372 : // TODO: this notification method here is used by UndoManagerHelper_Impl, to multiplex the notifications we
373 : // receive from the IUndoManager. Those notitications are sent with a locked SolarMutex, which means
374 : // we're doing the multiplexing here with a locked SM, too. Which is Bad (TM).
375 : // Fixing this properly would require outsourcing all the notifications into an own thread - which might lead
376 : // to problems of its own, since clients might expect synchronous notifications.
377 :
378 0 : m_aUndoListeners.notifyEach( i_notificationMethod, aEvent );
379 0 : impl_notifyModified();
380 0 : }
381 :
382 : //------------------------------------------------------------------------------------------------------------------
383 0 : void UndoManagerHelper_Impl::notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) )
384 : {
385 0 : const EventObject aEvent( getXUndoManager() );
386 :
387 : // TODO: the same comment as in the other notify, regarding SM locking applies here ...
388 :
389 0 : m_aUndoListeners.notifyEach( i_notificationMethod, aEvent );
390 0 : impl_notifyModified();
391 0 : }
392 :
393 : //------------------------------------------------------------------------------------------------------------------
394 0 : void UndoManagerHelper_Impl::enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock )
395 : {
396 : impl_processRequest(
397 : ::boost::bind(
398 : &UndoManagerHelper_Impl::impl_enterUndoContext,
399 : this,
400 : ::boost::cref( i_title ),
401 : i_hidden
402 : ),
403 : i_instanceLock
404 0 : );
405 0 : }
406 :
407 : //------------------------------------------------------------------------------------------------------------------
408 0 : void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard& i_instanceLock )
409 : {
410 : impl_processRequest(
411 : ::boost::bind(
412 : &UndoManagerHelper_Impl::impl_leaveUndoContext,
413 : this
414 : ),
415 : i_instanceLock
416 0 : );
417 0 : }
418 :
419 : //------------------------------------------------------------------------------------------------------------------
420 0 : void UndoManagerHelper_Impl::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
421 : {
422 0 : if ( !i_action.is() )
423 : throw IllegalArgumentException(
424 : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "illegal undo action object" ) ),
425 : getXUndoManager(),
426 : 1
427 0 : );
428 :
429 : impl_processRequest(
430 : ::boost::bind(
431 : &UndoManagerHelper_Impl::impl_addUndoAction,
432 : this,
433 : ::boost::ref( i_action )
434 : ),
435 : i_instanceLock
436 0 : );
437 0 : }
438 :
439 : //------------------------------------------------------------------------------------------------------------------
440 0 : void UndoManagerHelper_Impl::clear( IMutexGuard& i_instanceLock )
441 : {
442 : impl_processRequest(
443 : ::boost::bind(
444 : &UndoManagerHelper_Impl::impl_clear,
445 : this
446 : ),
447 : i_instanceLock
448 0 : );
449 0 : }
450 :
451 : //------------------------------------------------------------------------------------------------------------------
452 0 : void UndoManagerHelper_Impl::clearRedo( IMutexGuard& i_instanceLock )
453 : {
454 : impl_processRequest(
455 : ::boost::bind(
456 : &UndoManagerHelper_Impl::impl_clearRedo,
457 : this
458 : ),
459 : i_instanceLock
460 0 : );
461 0 : }
462 :
463 : //------------------------------------------------------------------------------------------------------------------
464 0 : void UndoManagerHelper_Impl::reset( IMutexGuard& i_instanceLock )
465 : {
466 : impl_processRequest(
467 : ::boost::bind(
468 : &UndoManagerHelper_Impl::impl_reset,
469 : this
470 : ),
471 : i_instanceLock
472 0 : );
473 0 : }
474 :
475 : //------------------------------------------------------------------------------------------------------------------
476 0 : void UndoManagerHelper_Impl::lock()
477 : {
478 : // SYNCHRONIZED --->
479 0 : ::osl::MutexGuard aGuard( getMutex() );
480 :
481 0 : if ( ++m_nLockCount == 1 )
482 : {
483 0 : IUndoManager& rUndoManager = getUndoManager();
484 0 : rUndoManager.EnableUndo( false );
485 0 : }
486 : // <--- SYNCHRONIZED
487 0 : }
488 :
489 : //------------------------------------------------------------------------------------------------------------------
490 0 : void UndoManagerHelper_Impl::unlock()
491 : {
492 : // SYNCHRONIZED --->
493 0 : ::osl::MutexGuard aGuard( getMutex() );
494 :
495 0 : if ( m_nLockCount == 0 )
496 0 : throw NotLockedException( "Undo manager is not locked", getXUndoManager() );
497 :
498 0 : if ( --m_nLockCount == 0 )
499 : {
500 0 : IUndoManager& rUndoManager = getUndoManager();
501 0 : rUndoManager.EnableUndo( true );
502 0 : }
503 : // <--- SYNCHRONIZED
504 0 : }
505 :
506 : //------------------------------------------------------------------------------------------------------------------
507 0 : void UndoManagerHelper_Impl::impl_processRequest( ::boost::function0< void > const& i_request, IMutexGuard& i_instanceLock )
508 : {
509 : // create the request, and add it to our queue
510 0 : ::rtl::Reference< UndoManagerRequest > pRequest( new UndoManagerRequest( i_request ) );
511 : {
512 0 : ::osl::MutexGuard aQueueGuard( m_aQueueMutex );
513 0 : m_aEventQueue.push( pRequest );
514 : }
515 :
516 0 : i_instanceLock.clear();
517 :
518 0 : if ( m_bProcessingEvents )
519 : {
520 : // another thread is processing the event queue currently => it will also process the event which we just added
521 0 : pRequest->wait();
522 : return;
523 : }
524 :
525 0 : m_bProcessingEvents = true;
526 0 : do
527 : {
528 0 : pRequest.clear();
529 : {
530 0 : ::osl::MutexGuard aQueueGuard( m_aQueueMutex );
531 0 : if ( m_aEventQueue.empty() )
532 : {
533 : // reset the flag before releasing the queue mutex, otherwise it's possible that another thread
534 : // could add an event after we release the mutex, but before we reset the flag. If then this other
535 : // thread checks the flag before be reset it, this thread's event would starve.
536 0 : m_bProcessingEvents = false;
537 : return;
538 : }
539 0 : pRequest = m_aEventQueue.front();
540 0 : m_aEventQueue.pop();
541 : }
542 : try
543 : {
544 0 : pRequest->execute();
545 0 : pRequest->wait();
546 : }
547 0 : catch( ... )
548 : {
549 : {
550 : // no chance to process further requests, if the current one failed
551 : // => discard them
552 0 : ::osl::MutexGuard aQueueGuard( m_aQueueMutex );
553 0 : while ( !m_aEventQueue.empty() )
554 : {
555 0 : pRequest = m_aEventQueue.front();
556 0 : m_aEventQueue.pop();
557 0 : pRequest->cancel( getXUndoManager() );
558 : }
559 0 : m_bProcessingEvents = false;
560 : }
561 : // re-throw the error
562 0 : throw;
563 : }
564 : }
565 0 : while ( true );
566 : }
567 :
568 : //------------------------------------------------------------------------------------------------------------------
569 0 : void UndoManagerHelper_Impl::impl_enterUndoContext( const ::rtl::OUString& i_title, const bool i_hidden )
570 : {
571 : // SYNCHRONIZED --->
572 0 : ::osl::ClearableMutexGuard aGuard( m_aMutex );
573 :
574 0 : IUndoManager& rUndoManager = getUndoManager();
575 0 : if ( !rUndoManager.IsUndoEnabled() )
576 : // ignore this request if the manager is locked
577 0 : return;
578 :
579 0 : if ( i_hidden && ( rUndoManager.GetUndoActionCount( IUndoManager::CurrentLevel ) == 0 ) )
580 : throw EmptyUndoStackException(
581 : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "can't enter a hidden context without a previous Undo action" ) ),
582 0 : m_rUndoManagerImplementation.getThis()
583 0 : );
584 :
585 : {
586 0 : ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
587 0 : rUndoManager.EnterListAction( i_title, ::rtl::OUString() );
588 : }
589 :
590 0 : m_aContextVisibilities.push( i_hidden );
591 :
592 0 : const UndoManagerEvent aEvent( buildEvent( i_title ) );
593 0 : aGuard.clear();
594 : // <--- SYNCHRONIZED
595 :
596 0 : m_aUndoListeners.notifyEach( i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, aEvent );
597 0 : impl_notifyModified();
598 : }
599 :
600 : //------------------------------------------------------------------------------------------------------------------
601 0 : void UndoManagerHelper_Impl::impl_leaveUndoContext()
602 : {
603 : // SYNCHRONIZED --->
604 0 : ::osl::ClearableMutexGuard aGuard( m_aMutex );
605 :
606 0 : IUndoManager& rUndoManager = getUndoManager();
607 0 : if ( !rUndoManager.IsUndoEnabled() )
608 : // ignore this request if the manager is locked
609 0 : return;
610 :
611 0 : if ( !rUndoManager.IsInListAction() )
612 : throw InvalidStateException(
613 : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "no active undo context" ) ),
614 : getXUndoManager()
615 0 : );
616 :
617 0 : size_t nContextElements = 0;
618 :
619 0 : const bool isHiddenContext = m_aContextVisibilities.top();;
620 0 : m_aContextVisibilities.pop();
621 :
622 0 : const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::TopLevel ) > 0 );
623 : {
624 0 : ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
625 0 : if ( isHiddenContext )
626 0 : nContextElements = rUndoManager.LeaveAndMergeListAction();
627 : else
628 0 : nContextElements = rUndoManager.LeaveListAction();
629 : }
630 0 : const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::TopLevel ) > 0 );
631 :
632 : // prepare notification
633 0 : void ( SAL_CALL XUndoManagerListener::*notificationMethod )( const UndoManagerEvent& ) = NULL;
634 :
635 0 : UndoManagerEvent aContextEvent( buildEvent( ::rtl::OUString() ) );
636 0 : const EventObject aClearedEvent( getXUndoManager() );
637 0 : if ( nContextElements == 0 )
638 : {
639 0 : notificationMethod = &XUndoManagerListener::cancelledContext;
640 : }
641 0 : else if ( isHiddenContext )
642 : {
643 0 : notificationMethod = &XUndoManagerListener::leftHiddenContext;
644 : }
645 : else
646 : {
647 0 : aContextEvent.UndoActionTitle = rUndoManager.GetUndoActionComment( 0, IUndoManager::CurrentLevel );
648 0 : notificationMethod = &XUndoManagerListener::leftContext;
649 : }
650 :
651 0 : aGuard.clear();
652 : // <--- SYNCHRONIZED
653 :
654 0 : if ( bHadRedoActions && !bHasRedoActions )
655 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aClearedEvent );
656 0 : m_aUndoListeners.notifyEach( notificationMethod, aContextEvent );
657 0 : impl_notifyModified();
658 : }
659 :
660 : //------------------------------------------------------------------------------------------------------------------
661 0 : void UndoManagerHelper_Impl::impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo )
662 : {
663 0 : ::osl::Guard< ::framework::IMutex > aExternalGuard( i_externalLock.getGuardedMutex() );
664 : // note that this assumes that the mutex has been released in the thread which added the
665 : // Undo/Redo request, so we can successfully acquire it
666 :
667 : // SYNCHRONIZED --->
668 0 : ::osl::ClearableMutexGuard aGuard( m_aMutex );
669 :
670 0 : IUndoManager& rUndoManager = getUndoManager();
671 0 : if ( rUndoManager.IsInListAction() )
672 0 : throw UndoContextNotClosedException( ::rtl::OUString(), getXUndoManager() );
673 :
674 : const size_t nElements = i_undo
675 0 : ? rUndoManager.GetUndoActionCount( IUndoManager::TopLevel )
676 0 : : rUndoManager.GetRedoActionCount( IUndoManager::TopLevel );
677 0 : if ( nElements == 0 )
678 0 : throw EmptyUndoStackException( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "stack is empty" )), getXUndoManager() );
679 :
680 0 : aGuard.clear();
681 : // <--- SYNCHRONIZED
682 :
683 : try
684 : {
685 0 : if ( i_undo )
686 0 : rUndoManager.Undo();
687 : else
688 0 : rUndoManager.Redo();
689 : }
690 0 : catch( const RuntimeException& ) { /* allowed to leave here */ throw; }
691 0 : catch( const UndoFailedException& ) { /* allowed to leave here */ throw; }
692 0 : catch( const Exception& )
693 : {
694 : // not allowed to leave
695 0 : const Any aError( ::cppu::getCaughtException() );
696 0 : throw UndoFailedException( ::rtl::OUString(), getXUndoManager(), aError );
697 0 : }
698 :
699 : // note that in opposite to all of the other methods, we do *not* have our mutex locked when calling
700 : // into the IUndoManager implementation. This ensures that an actual XUndoAction::undo/redo is also
701 : // called without our mutex being locked.
702 : // As a consequence, we do not set m_bAPIActionRunning here. Instead, our actionUndone/actionRedone methods
703 : // *always* multiplex the event to our XUndoManagerListeners, not only when m_bAPIActionRunning is FALSE (This
704 : // again is different from all other SfxUndoListener methods).
705 : // So, we do not need to do this notification here ourself.
706 0 : }
707 :
708 : //------------------------------------------------------------------------------------------------------------------
709 0 : void UndoManagerHelper_Impl::impl_addUndoAction( const Reference< XUndoAction >& i_action )
710 : {
711 : // SYNCHRONIZED --->
712 0 : ::osl::ClearableMutexGuard aGuard( m_aMutex );
713 :
714 0 : IUndoManager& rUndoManager = getUndoManager();
715 0 : if ( !rUndoManager.IsUndoEnabled() )
716 : // ignore the request if the manager is locked
717 0 : return;
718 :
719 0 : const UndoManagerEvent aEventAdd( buildEvent( i_action->getTitle() ) );
720 0 : const EventObject aEventClear( getXUndoManager() );
721 :
722 0 : const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::CurrentLevel ) > 0 );
723 : {
724 0 : ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
725 0 : rUndoManager.AddUndoAction( new UndoActionWrapper( i_action ) );
726 : }
727 0 : const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( IUndoManager::CurrentLevel ) > 0 );
728 :
729 0 : aGuard.clear();
730 : // <--- SYNCHRONIZED
731 :
732 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::undoActionAdded, aEventAdd );
733 0 : if ( bHadRedoActions && !bHasRedoActions )
734 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEventClear );
735 0 : impl_notifyModified();
736 : }
737 :
738 : //------------------------------------------------------------------------------------------------------------------
739 0 : void UndoManagerHelper_Impl::impl_clear()
740 : {
741 : // SYNCHRONIZED --->
742 0 : ::osl::ClearableMutexGuard aGuard( m_aMutex );
743 :
744 0 : IUndoManager& rUndoManager = getUndoManager();
745 0 : if ( rUndoManager.IsInListAction() )
746 0 : throw UndoContextNotClosedException( ::rtl::OUString(), getXUndoManager() );
747 :
748 : {
749 0 : ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
750 0 : rUndoManager.Clear();
751 : }
752 :
753 0 : const EventObject aEvent( getXUndoManager() );
754 0 : aGuard.clear();
755 : // <--- SYNCHRONIZED
756 :
757 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::allActionsCleared, aEvent );
758 0 : impl_notifyModified();
759 0 : }
760 :
761 : //------------------------------------------------------------------------------------------------------------------
762 0 : void UndoManagerHelper_Impl::impl_clearRedo()
763 : {
764 : // SYNCHRONIZED --->
765 0 : ::osl::ClearableMutexGuard aGuard( m_aMutex );
766 :
767 0 : IUndoManager& rUndoManager = getUndoManager();
768 0 : if ( rUndoManager.IsInListAction() )
769 0 : throw UndoContextNotClosedException( ::rtl::OUString(), getXUndoManager() );
770 :
771 : {
772 0 : ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
773 0 : rUndoManager.ClearRedo();
774 : }
775 :
776 0 : const EventObject aEvent( getXUndoManager() );
777 0 : aGuard.clear();
778 : // <--- SYNCHRONIZED
779 :
780 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::redoActionsCleared, aEvent );
781 0 : impl_notifyModified();
782 0 : }
783 :
784 : //------------------------------------------------------------------------------------------------------------------
785 0 : void UndoManagerHelper_Impl::impl_reset()
786 : {
787 : // SYNCHRONIZED --->
788 0 : ::osl::ClearableMutexGuard aGuard( m_aMutex );
789 :
790 0 : IUndoManager& rUndoManager = getUndoManager();
791 : {
792 0 : ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
793 0 : rUndoManager.Reset();
794 : }
795 :
796 0 : const EventObject aEvent( getXUndoManager() );
797 0 : aGuard.clear();
798 : // <--- SYNCHRONIZED
799 :
800 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::resetAll, aEvent );
801 0 : impl_notifyModified();
802 0 : }
803 :
804 : //------------------------------------------------------------------------------------------------------------------
805 0 : void UndoManagerHelper_Impl::actionUndone( const String& i_actionComment )
806 : {
807 0 : UndoManagerEvent aEvent;
808 0 : aEvent.Source = getXUndoManager();
809 0 : aEvent.UndoActionTitle = i_actionComment;
810 0 : aEvent.UndoContextDepth = 0; // Undo can happen on level 0 only
811 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::actionUndone, aEvent );
812 0 : impl_notifyModified();
813 0 : }
814 :
815 : //------------------------------------------------------------------------------------------------------------------
816 0 : void UndoManagerHelper_Impl::actionRedone( const String& i_actionComment )
817 : {
818 0 : UndoManagerEvent aEvent;
819 0 : aEvent.Source = getXUndoManager();
820 0 : aEvent.UndoActionTitle = i_actionComment;
821 0 : aEvent.UndoContextDepth = 0; // Redo can happen on level 0 only
822 0 : m_aUndoListeners.notifyEach( &XUndoManagerListener::actionRedone, aEvent );
823 0 : impl_notifyModified();
824 0 : }
825 :
826 : //------------------------------------------------------------------------------------------------------------------
827 0 : void UndoManagerHelper_Impl::undoActionAdded( const String& i_actionComment )
828 : {
829 0 : if ( m_bAPIActionRunning )
830 0 : return;
831 :
832 0 : notify( i_actionComment, &XUndoManagerListener::undoActionAdded );
833 : }
834 :
835 : //------------------------------------------------------------------------------------------------------------------
836 0 : void UndoManagerHelper_Impl::cleared()
837 : {
838 0 : if ( m_bAPIActionRunning )
839 0 : return;
840 :
841 0 : notify( &XUndoManagerListener::allActionsCleared );
842 : }
843 :
844 : //------------------------------------------------------------------------------------------------------------------
845 0 : void UndoManagerHelper_Impl::clearedRedo()
846 : {
847 0 : if ( m_bAPIActionRunning )
848 0 : return;
849 :
850 0 : notify( &XUndoManagerListener::redoActionsCleared );
851 : }
852 :
853 : //------------------------------------------------------------------------------------------------------------------
854 0 : void UndoManagerHelper_Impl::resetAll()
855 : {
856 0 : if ( m_bAPIActionRunning )
857 0 : return;
858 :
859 0 : notify( &XUndoManagerListener::resetAll );
860 : }
861 :
862 : //------------------------------------------------------------------------------------------------------------------
863 0 : void UndoManagerHelper_Impl::listActionEntered( const String& i_comment )
864 : {
865 : #if OSL_DEBUG_LEVEL > 0
866 : m_aContextAPIFlags.push( m_bAPIActionRunning );
867 : #endif
868 :
869 0 : if ( m_bAPIActionRunning )
870 0 : return;
871 :
872 0 : notify( i_comment, &XUndoManagerListener::enteredContext );
873 : }
874 :
875 : //------------------------------------------------------------------------------------------------------------------
876 0 : void UndoManagerHelper_Impl::listActionLeft( const String& i_comment )
877 : {
878 : #if OSL_DEBUG_LEVEL > 0
879 : const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
880 : m_aContextAPIFlags.pop();
881 : OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" );
882 : #endif
883 :
884 0 : if ( m_bAPIActionRunning )
885 0 : return;
886 :
887 0 : notify( i_comment, &XUndoManagerListener::leftContext );
888 : }
889 :
890 : //------------------------------------------------------------------------------------------------------------------
891 0 : void UndoManagerHelper_Impl::listActionLeftAndMerged()
892 : {
893 : #if OSL_DEBUG_LEVEL > 0
894 : const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
895 : m_aContextAPIFlags.pop();
896 : OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeftAndMerged: API and non-API contexts interwoven!" );
897 : #endif
898 :
899 0 : if ( m_bAPIActionRunning )
900 0 : return;
901 :
902 0 : notify( &XUndoManagerListener::leftHiddenContext );
903 : }
904 :
905 : //------------------------------------------------------------------------------------------------------------------
906 0 : void UndoManagerHelper_Impl::listActionCancelled()
907 : {
908 : #if OSL_DEBUG_LEVEL > 0
909 : const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
910 : m_aContextAPIFlags.pop();
911 : OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" );
912 : #endif
913 :
914 0 : if ( m_bAPIActionRunning )
915 0 : return;
916 :
917 0 : notify( &XUndoManagerListener::cancelledContext );
918 : }
919 :
920 : //------------------------------------------------------------------------------------------------------------------
921 0 : void UndoManagerHelper_Impl::undoManagerDying()
922 : {
923 : // TODO: do we need to care? Or is this the responsibility of our owner?
924 0 : }
925 :
926 : //==================================================================================================================
927 : //= UndoManagerHelper
928 : //==================================================================================================================
929 : //------------------------------------------------------------------------------------------------------------------
930 1 : UndoManagerHelper::UndoManagerHelper( IUndoManagerImplementation& i_undoManagerImpl )
931 1 : :m_pImpl( new UndoManagerHelper_Impl( i_undoManagerImpl ) )
932 : {
933 1 : }
934 :
935 : //------------------------------------------------------------------------------------------------------------------
936 0 : UndoManagerHelper::~UndoManagerHelper()
937 : {
938 0 : }
939 :
940 : //------------------------------------------------------------------------------------------------------------------
941 1 : void UndoManagerHelper::disposing()
942 : {
943 1 : m_pImpl->disposing();
944 1 : }
945 :
946 : //------------------------------------------------------------------------------------------------------------------
947 0 : void UndoManagerHelper::enterUndoContext( const ::rtl::OUString& i_title, IMutexGuard& i_instanceLock )
948 : {
949 0 : m_pImpl->enterUndoContext( i_title, false, i_instanceLock );
950 0 : }
951 :
952 : //------------------------------------------------------------------------------------------------------------------
953 0 : void UndoManagerHelper::enterHiddenUndoContext( IMutexGuard& i_instanceLock )
954 : {
955 0 : m_pImpl->enterUndoContext( ::rtl::OUString(), true, i_instanceLock );
956 0 : }
957 :
958 : //------------------------------------------------------------------------------------------------------------------
959 0 : void UndoManagerHelper::leaveUndoContext( IMutexGuard& i_instanceLock )
960 : {
961 0 : m_pImpl->leaveUndoContext( i_instanceLock );
962 0 : }
963 :
964 : //------------------------------------------------------------------------------------------------------------------
965 0 : void UndoManagerHelper_Impl::undo( IMutexGuard& i_instanceLock )
966 : {
967 : impl_processRequest(
968 : ::boost::bind(
969 : &UndoManagerHelper_Impl::impl_doUndoRedo,
970 : this,
971 : ::boost::ref( i_instanceLock ),
972 : true
973 : ),
974 : i_instanceLock
975 0 : );
976 0 : }
977 :
978 : //------------------------------------------------------------------------------------------------------------------
979 0 : void UndoManagerHelper_Impl::redo( IMutexGuard& i_instanceLock )
980 : {
981 : impl_processRequest(
982 : ::boost::bind(
983 : &UndoManagerHelper_Impl::impl_doUndoRedo,
984 : this,
985 : ::boost::ref( i_instanceLock ),
986 : false
987 : ),
988 : i_instanceLock
989 0 : );
990 0 : }
991 :
992 : //------------------------------------------------------------------------------------------------------------------
993 0 : void UndoManagerHelper::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
994 : {
995 0 : m_pImpl->addUndoAction( i_action, i_instanceLock );
996 0 : }
997 :
998 : //------------------------------------------------------------------------------------------------------------------
999 0 : void UndoManagerHelper::undo( IMutexGuard& i_instanceLock )
1000 : {
1001 0 : m_pImpl->undo( i_instanceLock );
1002 0 : }
1003 :
1004 : //------------------------------------------------------------------------------------------------------------------
1005 0 : void UndoManagerHelper::redo( IMutexGuard& i_instanceLock )
1006 : {
1007 0 : m_pImpl->redo( i_instanceLock );
1008 0 : }
1009 :
1010 : //------------------------------------------------------------------------------------------------------------------
1011 0 : ::sal_Bool UndoManagerHelper::isUndoPossible() const
1012 : {
1013 : // SYNCHRONIZED --->
1014 0 : ::osl::MutexGuard aGuard( m_pImpl->getMutex() );
1015 0 : IUndoManager& rUndoManager = m_pImpl->getUndoManager();
1016 0 : if ( rUndoManager.IsInListAction() )
1017 0 : return sal_False;
1018 0 : return rUndoManager.GetUndoActionCount( IUndoManager::TopLevel ) > 0;
1019 : // <--- SYNCHRONIZED
1020 : }
1021 :
1022 : //------------------------------------------------------------------------------------------------------------------
1023 0 : ::sal_Bool UndoManagerHelper::isRedoPossible() const
1024 : {
1025 : // SYNCHRONIZED --->
1026 0 : ::osl::MutexGuard aGuard( m_pImpl->getMutex() );
1027 0 : const IUndoManager& rUndoManager = m_pImpl->getUndoManager();
1028 0 : if ( rUndoManager.IsInListAction() )
1029 0 : return sal_False;
1030 0 : return rUndoManager.GetRedoActionCount( IUndoManager::TopLevel ) > 0;
1031 : // <--- SYNCHRONIZED
1032 : }
1033 :
1034 : //------------------------------------------------------------------------------------------------------------------
1035 : namespace
1036 : {
1037 : //..............................................................................................................
1038 0 : ::rtl::OUString lcl_getCurrentActionTitle( UndoManagerHelper_Impl& i_impl, const bool i_undo )
1039 : {
1040 : // SYNCHRONIZED --->
1041 0 : ::osl::MutexGuard aGuard( i_impl.getMutex() );
1042 :
1043 0 : const IUndoManager& rUndoManager = i_impl.getUndoManager();
1044 : const size_t nActionCount = i_undo
1045 0 : ? rUndoManager.GetUndoActionCount( IUndoManager::TopLevel )
1046 0 : : rUndoManager.GetRedoActionCount( IUndoManager::TopLevel );
1047 0 : if ( nActionCount == 0 )
1048 : throw EmptyUndoStackException(
1049 : i_undo ? ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "no action on the undo stack" ) )
1050 : : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "no action on the redo stack" ) ),
1051 : i_impl.getXUndoManager()
1052 0 : );
1053 : return i_undo
1054 0 : ? rUndoManager.GetUndoActionComment( 0, IUndoManager::TopLevel )
1055 0 : : rUndoManager.GetRedoActionComment( 0, IUndoManager::TopLevel );
1056 : // <--- SYNCHRONIZED
1057 : }
1058 :
1059 : //..............................................................................................................
1060 0 : Sequence< ::rtl::OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl, const bool i_undo )
1061 : {
1062 : // SYNCHRONIZED --->
1063 0 : ::osl::MutexGuard aGuard( i_impl.getMutex() );
1064 :
1065 0 : const IUndoManager& rUndoManager = i_impl.getUndoManager();
1066 : const size_t nCount = i_undo
1067 0 : ? rUndoManager.GetUndoActionCount( IUndoManager::TopLevel )
1068 0 : : rUndoManager.GetRedoActionCount( IUndoManager::TopLevel );
1069 :
1070 0 : Sequence< ::rtl::OUString > aTitles( nCount );
1071 0 : for ( size_t i=0; i<nCount; ++i )
1072 : {
1073 0 : aTitles[i] = i_undo
1074 0 : ? rUndoManager.GetUndoActionComment( i, IUndoManager::TopLevel )
1075 0 : : rUndoManager.GetRedoActionComment( i, IUndoManager::TopLevel );
1076 : }
1077 0 : return aTitles;
1078 : // <--- SYNCHRONIZED
1079 : }
1080 : }
1081 :
1082 : //------------------------------------------------------------------------------------------------------------------
1083 0 : ::rtl::OUString UndoManagerHelper::getCurrentUndoActionTitle() const
1084 : {
1085 0 : return lcl_getCurrentActionTitle( *m_pImpl, true );
1086 : }
1087 :
1088 : //------------------------------------------------------------------------------------------------------------------
1089 0 : ::rtl::OUString UndoManagerHelper::getCurrentRedoActionTitle() const
1090 : {
1091 0 : return lcl_getCurrentActionTitle( *m_pImpl, false );
1092 : }
1093 :
1094 : //------------------------------------------------------------------------------------------------------------------
1095 0 : Sequence< ::rtl::OUString > UndoManagerHelper::getAllUndoActionTitles() const
1096 : {
1097 0 : return lcl_getAllActionTitles( *m_pImpl, true );
1098 : }
1099 :
1100 : //------------------------------------------------------------------------------------------------------------------
1101 0 : Sequence< ::rtl::OUString > UndoManagerHelper::getAllRedoActionTitles() const
1102 : {
1103 0 : return lcl_getAllActionTitles( *m_pImpl, false );
1104 : }
1105 :
1106 : //------------------------------------------------------------------------------------------------------------------
1107 0 : void UndoManagerHelper::clear( IMutexGuard& i_instanceLock )
1108 : {
1109 0 : m_pImpl->clear( i_instanceLock );
1110 0 : }
1111 :
1112 : //------------------------------------------------------------------------------------------------------------------
1113 0 : void UndoManagerHelper::clearRedo( IMutexGuard& i_instanceLock )
1114 : {
1115 0 : m_pImpl->clearRedo( i_instanceLock );
1116 0 : }
1117 :
1118 : //------------------------------------------------------------------------------------------------------------------
1119 0 : void UndoManagerHelper::reset( IMutexGuard& i_instanceLock )
1120 : {
1121 0 : m_pImpl->reset( i_instanceLock );
1122 0 : }
1123 :
1124 : //------------------------------------------------------------------------------------------------------------------
1125 0 : void UndoManagerHelper::lock()
1126 : {
1127 0 : m_pImpl->lock();
1128 0 : }
1129 :
1130 : //------------------------------------------------------------------------------------------------------------------
1131 0 : void UndoManagerHelper::unlock()
1132 : {
1133 0 : m_pImpl->unlock();
1134 0 : }
1135 :
1136 : //------------------------------------------------------------------------------------------------------------------
1137 0 : ::sal_Bool UndoManagerHelper::isLocked()
1138 : {
1139 : // SYNCHRONIZED --->
1140 0 : ::osl::MutexGuard aGuard( m_pImpl->getMutex() );
1141 :
1142 0 : IUndoManager& rUndoManager = m_pImpl->getUndoManager();
1143 0 : return !rUndoManager.IsUndoEnabled();
1144 : // <--- SYNCHRONIZED
1145 : }
1146 :
1147 : //------------------------------------------------------------------------------------------------------------------
1148 1 : void UndoManagerHelper::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
1149 : {
1150 1 : if ( i_listener.is() )
1151 1 : m_pImpl->addUndoManagerListener( i_listener );
1152 1 : }
1153 :
1154 : //------------------------------------------------------------------------------------------------------------------
1155 1 : void UndoManagerHelper::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
1156 : {
1157 1 : if ( i_listener.is() )
1158 1 : m_pImpl->removeUndoManagerListener( i_listener );
1159 1 : }
1160 :
1161 : //------------------------------------------------------------------------------------------------------------------
1162 0 : void UndoManagerHelper::addModifyListener( const Reference< XModifyListener >& i_listener )
1163 : {
1164 0 : if ( i_listener.is() )
1165 0 : m_pImpl->addModifyListener( i_listener );
1166 0 : }
1167 :
1168 : //------------------------------------------------------------------------------------------------------------------
1169 0 : void UndoManagerHelper::removeModifyListener( const Reference< XModifyListener >& i_listener )
1170 : {
1171 0 : if ( i_listener.is() )
1172 0 : m_pImpl->removeModifyListener( i_listener );
1173 0 : }
1174 :
1175 : //......................................................................................................................
1176 66 : } // namespace framework
1177 : //......................................................................................................................
1178 :
1179 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|