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 #include "precompiled_framework.hxx"
25 
26 #include "framework/documentundoguard.hxx"
27 
28 /** === begin UNO includes === **/
29 #include <com/sun/star/document/XUndoManagerSupplier.hpp>
30 /** === end UNO includes === **/
31 
32 #include <cppuhelper/implbase1.hxx>
33 #include <rtl/ref.hxx>
34 #include <tools/diagnose_ex.h>
35 
36 //......................................................................................................................
37 namespace framework
38 {
39 //......................................................................................................................
40 
41 	/** === begin UNO using === **/
42 	using ::com::sun::star::uno::Reference;
43 	using ::com::sun::star::uno::XInterface;
44 	using ::com::sun::star::uno::UNO_QUERY;
45 	using ::com::sun::star::uno::UNO_QUERY_THROW;
46 	using ::com::sun::star::uno::Exception;
47 	using ::com::sun::star::uno::RuntimeException;
48 	using ::com::sun::star::uno::Any;
49 	using ::com::sun::star::uno::makeAny;
50 	using ::com::sun::star::uno::Sequence;
51 	using ::com::sun::star::uno::Type;
52     using ::com::sun::star::document::XUndoManagerSupplier;
53     using ::com::sun::star::document::XUndoManager;
54     using ::com::sun::star::document::XUndoManagerListener;
55     using ::com::sun::star::document::UndoManagerEvent;
56     using ::com::sun::star::lang::EventObject;
57 	/** === end UNO using === **/
58 
59 	//==================================================================================================================
60 	//= UndoManagerContextListener
61 	//==================================================================================================================
62     typedef ::cppu::WeakImplHelper1 <   XUndoManagerListener
63                                     >   UndoManagerContextListener_Base;
64     class UndoManagerContextListener : public UndoManagerContextListener_Base
65     {
66     public:
UndoManagerContextListener(const Reference<XUndoManager> & i_undoManager)67         UndoManagerContextListener( const Reference< XUndoManager >& i_undoManager )
68             :m_xUndoManager( i_undoManager, UNO_QUERY_THROW )
69             ,m_nRelativeContextDepth( 0 )
70             ,m_documentDisposed( false )
71         {
72             osl_incrementInterlockedCount( &m_refCount );
73             {
74                 m_xUndoManager->addUndoManagerListener( this );
75             }
76             osl_decrementInterlockedCount( &m_refCount );
77         }
78 
UndoManagerContextListener()79         UndoManagerContextListener()
80         {
81         }
82 
finish()83         void finish()
84         {
85             OSL_ENSURE( m_nRelativeContextDepth >= 0, "UndoManagerContextListener: more contexts left than entered?" );
86 
87             if ( m_documentDisposed )
88                 return;
89 
90             // work with a copy of m_nRelativeContextDepth, to be independent from possible bugs in the
91             // listener notifications (where it would be decremented with every leaveUndoContext)
92             sal_Int32 nDepth = m_nRelativeContextDepth;
93             while ( nDepth-- > 0 )
94             {
95                 m_xUndoManager->leaveUndoContext();
96             }
97             m_xUndoManager->removeUndoManagerListener( this );
98         }
99 
100         // XUndoManagerListener
101         virtual void SAL_CALL undoActionAdded( const UndoManagerEvent& i_event ) throw (RuntimeException);
102         virtual void SAL_CALL actionUndone( const UndoManagerEvent& i_event ) throw (RuntimeException);
103         virtual void SAL_CALL actionRedone( const UndoManagerEvent& i_event ) throw (RuntimeException);
104         virtual void SAL_CALL allActionsCleared( const EventObject& i_event ) throw (RuntimeException);
105         virtual void SAL_CALL redoActionsCleared( const EventObject& i_event ) throw (RuntimeException);
106         virtual void SAL_CALL resetAll( const EventObject& i_event ) throw (RuntimeException);
107         virtual void SAL_CALL enteredContext( const UndoManagerEvent& i_event ) throw (RuntimeException);
108         virtual void SAL_CALL enteredHiddenContext( const UndoManagerEvent& i_event ) throw (RuntimeException);
109         virtual void SAL_CALL leftContext( const UndoManagerEvent& i_event ) throw (RuntimeException);
110         virtual void SAL_CALL leftHiddenContext( const UndoManagerEvent& i_event ) throw (RuntimeException);
111         virtual void SAL_CALL cancelledContext( const UndoManagerEvent& i_event ) throw (RuntimeException);
112 
113         // XEventListener
114         virtual void SAL_CALL disposing( const EventObject& i_event ) throw (RuntimeException);
115 
116     private:
117         Reference< XUndoManager > const m_xUndoManager;
118         oslInterlockedCount             m_nRelativeContextDepth;
119         bool                            m_documentDisposed;
120     };
121 
122     //------------------------------------------------------------------------------------------------------------------
undoActionAdded(const UndoManagerEvent & i_event)123     void SAL_CALL UndoManagerContextListener::undoActionAdded( const UndoManagerEvent& i_event ) throw (RuntimeException)
124     {
125         (void)i_event;
126         // not interested in
127     }
128 
129     //------------------------------------------------------------------------------------------------------------------
actionUndone(const UndoManagerEvent & i_event)130     void SAL_CALL UndoManagerContextListener::actionUndone( const UndoManagerEvent& i_event ) throw (RuntimeException)
131     {
132         (void)i_event;
133         // not interested in
134     }
135 
136     //------------------------------------------------------------------------------------------------------------------
actionRedone(const UndoManagerEvent & i_event)137     void SAL_CALL UndoManagerContextListener::actionRedone( const UndoManagerEvent& i_event ) throw (RuntimeException)
138     {
139         (void)i_event;
140         // not interested in
141     }
142 
143     //------------------------------------------------------------------------------------------------------------------
allActionsCleared(const EventObject & i_event)144     void SAL_CALL UndoManagerContextListener::allActionsCleared( const EventObject& i_event ) throw (RuntimeException)
145     {
146         (void)i_event;
147         // not interested in
148     }
149 
150     //------------------------------------------------------------------------------------------------------------------
redoActionsCleared(const EventObject & i_event)151     void SAL_CALL UndoManagerContextListener::redoActionsCleared( const EventObject& i_event ) throw (RuntimeException)
152     {
153         (void)i_event;
154         // not interested in
155     }
156 
157     //------------------------------------------------------------------------------------------------------------------
resetAll(const EventObject & i_event)158     void SAL_CALL UndoManagerContextListener::resetAll( const EventObject& i_event ) throw (RuntimeException)
159     {
160         (void)i_event;
161         m_nRelativeContextDepth = 0;
162     }
163 
164     //------------------------------------------------------------------------------------------------------------------
enteredContext(const UndoManagerEvent & i_event)165     void SAL_CALL UndoManagerContextListener::enteredContext( const UndoManagerEvent& i_event ) throw (RuntimeException)
166     {
167         (void)i_event;
168         osl_incrementInterlockedCount( &m_nRelativeContextDepth );
169     }
170 
171     //------------------------------------------------------------------------------------------------------------------
enteredHiddenContext(const UndoManagerEvent & i_event)172     void SAL_CALL UndoManagerContextListener::enteredHiddenContext( const UndoManagerEvent& i_event ) throw (RuntimeException)
173     {
174         (void)i_event;
175         osl_incrementInterlockedCount( &m_nRelativeContextDepth );
176     }
177 
178     //------------------------------------------------------------------------------------------------------------------
leftContext(const UndoManagerEvent & i_event)179     void SAL_CALL UndoManagerContextListener::leftContext( const UndoManagerEvent& i_event ) throw (RuntimeException)
180     {
181         (void)i_event;
182         osl_decrementInterlockedCount( &m_nRelativeContextDepth );
183     }
184 
185     //------------------------------------------------------------------------------------------------------------------
leftHiddenContext(const UndoManagerEvent & i_event)186     void SAL_CALL UndoManagerContextListener::leftHiddenContext( const UndoManagerEvent& i_event ) throw (RuntimeException)
187     {
188         (void)i_event;
189         osl_decrementInterlockedCount( &m_nRelativeContextDepth );
190     }
191 
192     //------------------------------------------------------------------------------------------------------------------
cancelledContext(const UndoManagerEvent & i_event)193     void SAL_CALL UndoManagerContextListener::cancelledContext( const UndoManagerEvent& i_event ) throw (RuntimeException)
194     {
195         (void)i_event;
196         osl_decrementInterlockedCount( &m_nRelativeContextDepth );
197     }
198 
199     //------------------------------------------------------------------------------------------------------------------
disposing(const EventObject & i_event)200     void SAL_CALL UndoManagerContextListener::disposing( const EventObject& i_event ) throw (RuntimeException)
201     {
202         (void)i_event;
203         m_documentDisposed = true;
204     }
205 
206 	//==================================================================================================================
207 	//= DocumentUndoGuard_Data
208 	//==================================================================================================================
209     struct DocumentUndoGuard_Data
210     {
211         Reference< XUndoManager >                       xUndoManager;
212         ::rtl::Reference< UndoManagerContextListener >  pContextListener;
213     };
214 
215     namespace
216     {
217 	    //--------------------------------------------------------------------------------------------------------------
lcl_init(DocumentUndoGuard_Data & i_data,const Reference<XInterface> & i_undoSupplierComponent)218         void lcl_init( DocumentUndoGuard_Data& i_data, const Reference< XInterface >& i_undoSupplierComponent )
219         {
220             try
221             {
222                 Reference< XUndoManagerSupplier > xUndoSupplier( i_undoSupplierComponent, UNO_QUERY );
223                 if ( xUndoSupplier.is() )
224                     i_data.xUndoManager.set( xUndoSupplier->getUndoManager(), UNO_QUERY_THROW );
225 
226                 if ( i_data.xUndoManager.is() )
227                     i_data.pContextListener.set( new UndoManagerContextListener( i_data.xUndoManager ) );
228             }
229             catch( const Exception& )
230             {
231         	    DBG_UNHANDLED_EXCEPTION();
232             }
233         }
234 
235         //--------------------------------------------------------------------------------------------------------------
lcl_restore(DocumentUndoGuard_Data & i_data)236         void lcl_restore( DocumentUndoGuard_Data& i_data )
237         {
238             try
239             {
240                 if ( i_data.pContextListener.is() )
241                     i_data.pContextListener->finish();
242                 i_data.pContextListener.clear();
243             }
244             catch( const Exception& )
245             {
246             	DBG_UNHANDLED_EXCEPTION();
247             }
248         }
249     }
250 
251 	//==================================================================================================================
252 	//= DocumentUndoGuard
253 	//==================================================================================================================
254 	//------------------------------------------------------------------------------------------------------------------
DocumentUndoGuard(const Reference<XInterface> & i_undoSupplierComponent)255     DocumentUndoGuard::DocumentUndoGuard( const Reference< XInterface >& i_undoSupplierComponent )
256         :m_pData( new DocumentUndoGuard_Data )
257     {
258         lcl_init( *m_pData, i_undoSupplierComponent );
259     }
260 
~DocumentUndoGuard()261     DocumentUndoGuard::~DocumentUndoGuard()
262     {
263         lcl_restore( *m_pData );
264     }
265 
266 //......................................................................................................................
267 } // namespace framework
268 //......................................................................................................................
269