1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sfx2.hxx"
26 
27 #include "docundomanager.hxx"
28 #include "sfx2/sfxbasemodel.hxx"
29 #include "sfx2/objsh.hxx"
30 #include "sfx2/viewfrm.hxx"
31 #include "sfx2/viewsh.hxx"
32 #include "sfx2/bindings.hxx"
33 
34 /** === begin UNO includes === **/
35 #include <com/sun/star/lang/XComponent.hpp>
36 /** === end UNO includes === **/
37 
38 #include <comphelper/anytostring.hxx>
39 #include <comphelper/flagguard.hxx>
40 #include <svl/undo.hxx>
41 #include <tools/diagnose_ex.h>
42 #include <framework/undomanagerhelper.hxx>
43 
44 #include <boost/noncopyable.hpp>
45 #include <stack>
46 
47 //......................................................................................................................
48 namespace sfx2
49 {
50 //......................................................................................................................
51 
52 	/** === begin UNO using === **/
53 	using ::com::sun::star::uno::Reference;
54 	using ::com::sun::star::uno::XInterface;
55 	using ::com::sun::star::uno::UNO_QUERY;
56 	using ::com::sun::star::uno::UNO_QUERY_THROW;
57 	using ::com::sun::star::uno::UNO_SET_THROW;
58 	using ::com::sun::star::uno::Exception;
59 	using ::com::sun::star::uno::RuntimeException;
60 	using ::com::sun::star::uno::Any;
61 	using ::com::sun::star::uno::makeAny;
62 	using ::com::sun::star::uno::Sequence;
63 	using ::com::sun::star::uno::Type;
64     using ::com::sun::star::util::InvalidStateException;
65     using ::com::sun::star::document::EmptyUndoStackException;
66     using ::com::sun::star::util::NotLockedException;
67     using ::com::sun::star::document::UndoContextNotClosedException;
68     using ::com::sun::star::document::XUndoAction;
69     using ::com::sun::star::document::XUndoManagerSupplier;
70     using ::com::sun::star::lang::XComponent;
71     using ::com::sun::star::lang::IllegalArgumentException;
72     using ::com::sun::star::lang::NotInitializedException;
73     using ::com::sun::star::lang::EventObject;
74     using ::com::sun::star::document::UndoManagerEvent;
75     using ::com::sun::star::document::XUndoManagerListener;
76     using ::com::sun::star::document::UndoFailedException;
77     using ::com::sun::star::document::XUndoManager;
78     using ::com::sun::star::lang::NoSupportException;
79     using ::com::sun::star::frame::XModel;
80 	/** === end UNO using === **/
81 
82     using ::svl::IUndoManager;
83 
84 	//==================================================================================================================
85 	//= DocumentUndoManager_Impl
86 	//==================================================================================================================
87     struct DocumentUndoManager_Impl : public ::framework::IUndoManagerImplementation
88     {
89         DocumentUndoManager&                rAntiImpl;
90         IUndoManager*                       pUndoManager;
91         ::framework::UndoManagerHelper      aUndoHelper;
92 
DocumentUndoManager_Implsfx2::DocumentUndoManager_Impl93         DocumentUndoManager_Impl( DocumentUndoManager& i_antiImpl )
94             :rAntiImpl( i_antiImpl )
95             ,pUndoManager( impl_retrieveUndoManager( i_antiImpl.getBaseModel() ) )
96                 // do this *before* the construction of aUndoHelper (which actually means: put pUndoManager before
97                 // aUndoHelper in the member list)!
98             ,aUndoHelper( *this )
99         {
100         }
101 
getObjectShellsfx2::DocumentUndoManager_Impl102         const SfxObjectShell* getObjectShell() const { return rAntiImpl.getBaseModel().GetObjectShell(); }
getObjectShellsfx2::DocumentUndoManager_Impl103               SfxObjectShell* getObjectShell()       { return rAntiImpl.getBaseModel().GetObjectShell(); }
104 
105         // IUndoManagerImplementation
106         virtual ::svl::IUndoManager&        getImplUndoManager();
107         virtual Reference< XUndoManager >   getThis();
108 
disposingsfx2::DocumentUndoManager_Impl109         void disposing()
110         {
111             aUndoHelper.disposing();
112             ENSURE_OR_RETURN_VOID( pUndoManager, "DocumentUndoManager_Impl::disposing: already disposed!" );
113             pUndoManager = NULL;
114         }
115 
116         void invalidateXDo_nolck();
117 
118     private:
impl_retrieveUndoManagersfx2::DocumentUndoManager_Impl119         static IUndoManager* impl_retrieveUndoManager( SfxBaseModel& i_baseModel )
120         {
121             IUndoManager* pUndoManager( NULL );
122             SfxObjectShell* pObjectShell = i_baseModel.GetObjectShell();
123             if ( pObjectShell != NULL )
124                 pUndoManager = pObjectShell->GetUndoManager();
125             if ( !pUndoManager )
126                 throw NotInitializedException( ::rtl::OUString(), *&i_baseModel );
127             return pUndoManager;
128         }
129     };
130 
131     //------------------------------------------------------------------------------------------------------------------
getImplUndoManager()132     ::svl::IUndoManager& DocumentUndoManager_Impl::getImplUndoManager()
133     {
134         ENSURE_OR_THROW( pUndoManager != NULL, "DocumentUndoManager_Impl::getImplUndoManager: no access to the doc's UndoManager implementation!" );
135 
136 #if OSL_DEBUG_LEVEL > 0
137         // in a non-product build, assert if the current UndoManager at the shell is not the same we obtained
138         // (and cached) at construction time
139         SfxObjectShell* pObjectShell = rAntiImpl.getBaseModel().GetObjectShell();
140         OSL_ENSURE( ( pObjectShell != NULL ) && ( pUndoManager == pObjectShell->GetUndoManager() ),
141             "DocumentUndoManager_Impl::getImplUndoManager: the UndoManager changed meanwhile - what about our listener?" );
142 #endif
143 
144         return *pUndoManager;
145     }
146 
147     //------------------------------------------------------------------------------------------------------------------
getThis()148     Reference< XUndoManager > DocumentUndoManager_Impl::getThis()
149     {
150         return static_cast< XUndoManager* >( &rAntiImpl );
151     }
152 
153     //------------------------------------------------------------------------------------------------------------------
invalidateXDo_nolck()154     void DocumentUndoManager_Impl::invalidateXDo_nolck()
155     {
156         SfxModelGuard aGuard( rAntiImpl );
157 
158         const SfxObjectShell* pDocShell = getObjectShell();
159         ENSURE_OR_THROW( pDocShell != NULL, "lcl_invalidateUndo: no access to the doc shell!" );
160         SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( pDocShell );
161         while ( pViewFrame )
162         {
163             pViewFrame->GetBindings().Invalidate( SID_UNDO );
164             pViewFrame->GetBindings().Invalidate( SID_REDO );
165             pViewFrame = SfxViewFrame::GetNext( *pViewFrame, pDocShell );
166         }
167     }
168 
169 	//==================================================================================================================
170 	//= SolarMutexFacade
171 	//==================================================================================================================
172     /** a facade for the SolarMutex, implementing ::framework::IMutex (as opposed to ::vos::IMutex)
173     */
174     class SolarMutexFacade : public ::framework::IMutex
175     {
176     public:
SolarMutexFacade()177         SolarMutexFacade()
178         {
179         }
180 
acquire()181         virtual void acquire()
182         {
183             Application::GetSolarMutex().acquire();
184         }
185 
release()186         virtual void release()
187         {
188             Application::GetSolarMutex().release();
189         }
190     };
191 
192 	//==================================================================================================================
193 	//= UndoManagerGuard
194 	//==================================================================================================================
195     class UndoManagerGuard  :public ::framework::IMutexGuard
196                             ,public ::boost::noncopyable
197     {
198     public:
UndoManagerGuard(DocumentUndoManager & i_undoManager)199         UndoManagerGuard( DocumentUndoManager& i_undoManager )
200             :m_guard( i_undoManager )
201             ,m_solarMutexFacade()
202         {
203         }
204 
~UndoManagerGuard()205         ~UndoManagerGuard()
206         {
207         }
208 
reset()209         virtual void reset()
210         {
211             m_guard.reset();
212         }
213 
clear()214         virtual void clear()
215         {
216             m_guard.clear();
217         }
218 
getGuardedMutex()219         virtual ::framework::IMutex& getGuardedMutex()
220         {
221             // note that this means that we *know* that SfxModelGuard also locks the SolarMutex (nothing more, nothing less).
222             // If this ever changes, we need to adjust this code here, too.
223             return m_solarMutexFacade;
224         }
225 
226     private:
227         SfxModelGuard       m_guard;
228         SolarMutexFacade    m_solarMutexFacade;
229     };
230 
231 	//==================================================================================================================
232 	//= DocumentUndoManager
233 	//==================================================================================================================
234 	//------------------------------------------------------------------------------------------------------------------
DocumentUndoManager(SfxBaseModel & i_document)235     DocumentUndoManager::DocumentUndoManager( SfxBaseModel& i_document )
236         :SfxModelSubComponent( i_document )
237         ,m_pImpl( new DocumentUndoManager_Impl( *this ) )
238     {
239     }
240 
241 	//------------------------------------------------------------------------------------------------------------------
~DocumentUndoManager()242     DocumentUndoManager::~DocumentUndoManager()
243     {
244     }
245 
246 	//------------------------------------------------------------------------------------------------------------------
disposing()247     void DocumentUndoManager::disposing()
248     {
249         m_pImpl->disposing();
250     }
251 
252     //------------------------------------------------------------------------------------------------------------------
isInContext() const253     bool DocumentUndoManager::isInContext() const
254     {
255         // No mutex locking within this method, no disposal check - this is the responsibility of the owner.
256         return m_pImpl->getImplUndoManager().IsInListAction();
257     }
258 
259     //------------------------------------------------------------------------------------------------------------------
acquire()260     void SAL_CALL DocumentUndoManager::acquire(  ) throw ()
261     {
262         SfxModelSubComponent::acquire();
263     }
264 
265     //------------------------------------------------------------------------------------------------------------------
release()266     void SAL_CALL DocumentUndoManager::release(  ) throw ()
267     {
268         SfxModelSubComponent::release();
269     }
270 
271     //------------------------------------------------------------------------------------------------------------------
enterUndoContext(const::rtl::OUString & i_title)272     void SAL_CALL DocumentUndoManager::enterUndoContext( const ::rtl::OUString& i_title ) throw (RuntimeException)
273     {
274         // SYNCHRONIZED --->
275         UndoManagerGuard aGuard( *this );
276         m_pImpl->aUndoHelper.enterUndoContext( i_title, aGuard );
277         // <--- SYNCHRONIZED
278         m_pImpl->invalidateXDo_nolck();
279     }
280 
281     //------------------------------------------------------------------------------------------------------------------
enterHiddenUndoContext()282     void SAL_CALL DocumentUndoManager::enterHiddenUndoContext(  ) throw (EmptyUndoStackException, RuntimeException)
283     {
284         // SYNCHRONIZED --->
285         UndoManagerGuard aGuard( *this );
286         m_pImpl->aUndoHelper.enterHiddenUndoContext( aGuard );
287         // <--- SYNCHRONIZED
288         m_pImpl->invalidateXDo_nolck();
289     }
290 
291     //------------------------------------------------------------------------------------------------------------------
leaveUndoContext()292     void SAL_CALL DocumentUndoManager::leaveUndoContext(  ) throw (InvalidStateException, RuntimeException)
293     {
294         // SYNCHRONIZED --->
295         UndoManagerGuard aGuard( *this );
296         m_pImpl->aUndoHelper.leaveUndoContext( aGuard );
297         // <--- SYNCHRONIZED
298         m_pImpl->invalidateXDo_nolck();
299     }
300 
301     //------------------------------------------------------------------------------------------------------------------
addUndoAction(const Reference<XUndoAction> & i_action)302     void SAL_CALL DocumentUndoManager::addUndoAction( const Reference< XUndoAction >& i_action ) throw (RuntimeException, IllegalArgumentException)
303     {
304         // SYNCHRONIZED --->
305         UndoManagerGuard aGuard( *this );
306         m_pImpl->aUndoHelper.addUndoAction( i_action, aGuard );
307         // <--- SYNCHRONIZED
308         m_pImpl->invalidateXDo_nolck();
309     }
310 
311     //------------------------------------------------------------------------------------------------------------------
undo()312     void SAL_CALL DocumentUndoManager::undo(  ) throw (EmptyUndoStackException, UndoContextNotClosedException, UndoFailedException, RuntimeException)
313     {
314         // SYNCHRONIZED --->
315         UndoManagerGuard aGuard( *this );
316         m_pImpl->aUndoHelper.undo( aGuard );
317         // <--- SYNCHRONIZED
318         m_pImpl->invalidateXDo_nolck();
319     }
320 
321     //------------------------------------------------------------------------------------------------------------------
redo()322     void SAL_CALL DocumentUndoManager::redo(  ) throw (EmptyUndoStackException, UndoContextNotClosedException, UndoFailedException, RuntimeException)
323     {
324         // SYNCHRONIZED --->
325         UndoManagerGuard aGuard( *this );
326         m_pImpl->aUndoHelper.redo( aGuard );
327         // <--- SYNCHRONIZED
328         m_pImpl->invalidateXDo_nolck();
329     }
330 
331     //------------------------------------------------------------------------------------------------------------------
isUndoPossible()332     ::sal_Bool SAL_CALL DocumentUndoManager::isUndoPossible(  ) throw (RuntimeException)
333     {
334         UndoManagerGuard aGuard( *this );
335         return m_pImpl->aUndoHelper.isUndoPossible();
336     }
337 
338     //------------------------------------------------------------------------------------------------------------------
isRedoPossible()339     ::sal_Bool SAL_CALL DocumentUndoManager::isRedoPossible(  ) throw (RuntimeException)
340     {
341         UndoManagerGuard aGuard( *this );
342         return m_pImpl->aUndoHelper.isRedoPossible();
343     }
344 
345     //------------------------------------------------------------------------------------------------------------------
getCurrentUndoActionTitle()346     ::rtl::OUString SAL_CALL DocumentUndoManager::getCurrentUndoActionTitle(  ) throw (EmptyUndoStackException, RuntimeException)
347     {
348         UndoManagerGuard aGuard( *this );
349         return m_pImpl->aUndoHelper.getCurrentUndoActionTitle();
350     }
351 
352     //------------------------------------------------------------------------------------------------------------------
getCurrentRedoActionTitle()353     ::rtl::OUString SAL_CALL DocumentUndoManager::getCurrentRedoActionTitle(  ) throw (EmptyUndoStackException, RuntimeException)
354     {
355         UndoManagerGuard aGuard( *this );
356         return m_pImpl->aUndoHelper.getCurrentRedoActionTitle();
357     }
358 
359     //------------------------------------------------------------------------------------------------------------------
getAllUndoActionTitles()360     Sequence< ::rtl::OUString > SAL_CALL DocumentUndoManager::getAllUndoActionTitles(  ) throw (RuntimeException)
361     {
362         UndoManagerGuard aGuard( *this );
363         return m_pImpl->aUndoHelper.getAllUndoActionTitles();
364     }
365 
366     //------------------------------------------------------------------------------------------------------------------
getAllRedoActionTitles()367     Sequence< ::rtl::OUString > SAL_CALL DocumentUndoManager::getAllRedoActionTitles(  ) throw (RuntimeException)
368     {
369         UndoManagerGuard aGuard( *this );
370         return m_pImpl->aUndoHelper.getAllRedoActionTitles();
371     }
372 
373     //------------------------------------------------------------------------------------------------------------------
clear()374     void SAL_CALL DocumentUndoManager::clear(  ) throw (UndoContextNotClosedException, RuntimeException)
375     {
376         // SYNCHRONIZED --->
377         UndoManagerGuard aGuard( *this );
378         m_pImpl->aUndoHelper.clear( aGuard );
379         // <--- SYNCHRONIZED
380         m_pImpl->invalidateXDo_nolck();
381     }
382 
383     //------------------------------------------------------------------------------------------------------------------
clearRedo()384     void SAL_CALL DocumentUndoManager::clearRedo(  ) throw (UndoContextNotClosedException, RuntimeException)
385     {
386         // SYNCHRONIZED --->
387         UndoManagerGuard aGuard( *this );
388         m_pImpl->aUndoHelper.clearRedo( aGuard );
389         // <--- SYNCHRONIZED
390         m_pImpl->invalidateXDo_nolck();
391     }
392 
393     //------------------------------------------------------------------------------------------------------------------
reset()394     void SAL_CALL DocumentUndoManager::reset() throw (RuntimeException)
395     {
396         // SYNCHRONIZED --->
397         UndoManagerGuard aGuard( *this );
398         m_pImpl->aUndoHelper.reset( aGuard );
399         // <--- SYNCHRONIZED
400         m_pImpl->invalidateXDo_nolck();
401     }
402 
403     //------------------------------------------------------------------------------------------------------------------
lock()404     void SAL_CALL DocumentUndoManager::lock(  ) throw (RuntimeException)
405     {
406         UndoManagerGuard aGuard( *this );
407         m_pImpl->aUndoHelper.lock();
408     }
409 
410     //------------------------------------------------------------------------------------------------------------------
unlock()411     void SAL_CALL DocumentUndoManager::unlock(  ) throw (RuntimeException, NotLockedException)
412     {
413         UndoManagerGuard aGuard( *this );
414         m_pImpl->aUndoHelper.unlock();
415     }
416 
417     //------------------------------------------------------------------------------------------------------------------
isLocked()418     ::sal_Bool SAL_CALL DocumentUndoManager::isLocked(  ) throw (RuntimeException)
419     {
420         UndoManagerGuard aGuard( *this );
421         return m_pImpl->aUndoHelper.isLocked();
422     }
423 
424     //------------------------------------------------------------------------------------------------------------------
addUndoManagerListener(const Reference<XUndoManagerListener> & i_listener)425     void SAL_CALL DocumentUndoManager::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) throw (RuntimeException)
426     {
427         UndoManagerGuard aGuard( *this );
428         return m_pImpl->aUndoHelper.addUndoManagerListener( i_listener );
429     }
430 
431     //------------------------------------------------------------------------------------------------------------------
removeUndoManagerListener(const Reference<XUndoManagerListener> & i_listener)432     void SAL_CALL DocumentUndoManager::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener ) throw (RuntimeException)
433     {
434         UndoManagerGuard aGuard( *this );
435         return m_pImpl->aUndoHelper.removeUndoManagerListener( i_listener );
436     }
437 
438     //------------------------------------------------------------------------------------------------------------------
getParent()439     Reference< XInterface > SAL_CALL DocumentUndoManager::getParent(  ) throw (RuntimeException)
440     {
441         UndoManagerGuard aGuard( *this );
442         return static_cast< XModel* >( &getBaseModel() );
443     }
444 
445     //------------------------------------------------------------------------------------------------------------------
setParent(const Reference<XInterface> & i_parent)446     void SAL_CALL DocumentUndoManager::setParent( const Reference< XInterface >& i_parent ) throw (NoSupportException, RuntimeException)
447     {
448         (void)i_parent;
449         throw NoSupportException( ::rtl::OUString(), m_pImpl->getThis() );
450     }
451 
452 //......................................................................................................................
453 } // namespace sfx2
454 //......................................................................................................................
455