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_basctl.hxx"
26 
27 #include "doceventnotifier.hxx"
28 #include "scriptdocument.hxx"
29 
30 /** === begin UNO includes === **/
31 #include <com/sun/star/document/XEventBroadcaster.hpp>
32 /** === end UNO includes === **/
33 
34 #include <vcl/svapp.hxx>
35 
36 #include <tools/diagnose_ex.h>
37 
38 #include <comphelper/componentcontext.hxx>
39 #include <comphelper/processfactory.hxx>
40 
41 #include <vos/mutex.hxx>
42 
43 #include <cppuhelper/compbase1.hxx>
44 #include <cppuhelper/basemutex.hxx>
45 
46 //........................................................................
47 namespace basctl
48 {
49 //........................................................................
50 
51 	/** === begin UNO using === **/
52     using ::com::sun::star::document::XEventBroadcaster;
53     using ::com::sun::star::document::XEventListener;
54     using ::com::sun::star::document::EventObject;
55     using ::com::sun::star::uno::RuntimeException;
56     using ::com::sun::star::uno::Reference;
57     using ::com::sun::star::uno::UNO_QUERY_THROW;
58     using ::com::sun::star::uno::Exception;
59     using ::com::sun::star::frame::XModel;
60     using ::com::sun::star::uno::UNO_QUERY;
61 	/** === end UNO using === **/
62     namespace csslang = ::com::sun::star::lang;
63 
64 	//====================================================================
65 	//= DocumentEventNotifier_Impl
66 	//====================================================================
67     typedef ::cppu::WeakComponentImplHelper1    <   XEventListener
68                                                 >   DocumentEventNotifier_Impl_Base;
69 
70     enum ListenerAction
71     {
72         RegisterListener,
73         RemoveListener
74     };
75 
76     /** impl class for DocumentEventNotifier
77     */
78     class DocumentEventNotifier_Impl    :public ::boost::noncopyable
79                                         ,public ::cppu::BaseMutex
80                                         ,public DocumentEventNotifier_Impl_Base
81     {
82     public:
83         DocumentEventNotifier_Impl( DocumentEventListener& _rListener, const Reference< XModel >& _rxDocument );
84 
85         // document::XEventListener
86         virtual void SAL_CALL notifyEvent( const EventObject& Event ) throw (RuntimeException);
87 
88         // lang::XEventListener
89         virtual void SAL_CALL disposing( const csslang::EventObject& Event ) throw (RuntimeException);
90 
91         // ComponentHelper
92         virtual void SAL_CALL disposing();
93 
94     protected:
95         ~DocumentEventNotifier_Impl();
96 
97     private:
98         /// determines whether the instance is already disposed
impl_isDisposed_nothrow() const99         bool    impl_isDisposed_nothrow() const { return m_pListener == NULL; }
100 
101         /// disposes the instance
102         void    impl_dispose_nothrow();
103 
104         /// registers or revokes the instance as listener at the global event broadcaster
105         void    impl_listenerAction_nothrow( ListenerAction _eAction );
106 
107     private:
108         DocumentEventListener*  m_pListener;
109         Reference< XModel >     m_xModel;
110     };
111 
112 	//--------------------------------------------------------------------
DocumentEventNotifier_Impl(DocumentEventListener & _rListener,const Reference<XModel> & _rxDocument)113     DocumentEventNotifier_Impl::DocumentEventNotifier_Impl( DocumentEventListener& _rListener, const Reference< XModel >& _rxDocument )
114         :DocumentEventNotifier_Impl_Base( m_aMutex )
115         ,m_pListener( &_rListener )
116         ,m_xModel( _rxDocument )
117     {
118         osl_incrementInterlockedCount( &m_refCount );
119         impl_listenerAction_nothrow( RegisterListener );
120         osl_decrementInterlockedCount( &m_refCount );
121     }
122 
123 	//--------------------------------------------------------------------
~DocumentEventNotifier_Impl()124     DocumentEventNotifier_Impl::~DocumentEventNotifier_Impl()
125     {
126         if ( !impl_isDisposed_nothrow() )
127         {
128             acquire();
129             dispose();
130         }
131     }
132 
133     //--------------------------------------------------------------------
notifyEvent(const EventObject & _rEvent)134     void SAL_CALL DocumentEventNotifier_Impl::notifyEvent( const EventObject& _rEvent ) throw (RuntimeException)
135     {
136         ::osl::ClearableMutexGuard aGuard( m_aMutex );
137 
138         OSL_PRECOND( !impl_isDisposed_nothrow(), "DocumentEventNotifier_Impl::notifyEvent: disposed, but still getting events?" );
139         if ( impl_isDisposed_nothrow() )
140             return;
141 
142         Reference< XModel > xDocument( _rEvent.Source, UNO_QUERY );
143         OSL_ENSURE( xDocument.is(), "DocumentEventNotifier_Impl::notifyEvent: illegal source document!" );
144         if ( !xDocument.is() )
145             return;
146 
147         struct EventEntry
148         {
149             const sal_Char* pEventName;
150             void (DocumentEventListener::*listenerMethod)( const ScriptDocument& _rDocument );
151         };
152         EventEntry aEvents[] = {
153             { "OnNew",          &DocumentEventListener::onDocumentCreated },
154             { "OnLoad",         &DocumentEventListener::onDocumentOpened },
155             { "OnSave",         &DocumentEventListener::onDocumentSave },
156             { "OnSaveDone",     &DocumentEventListener::onDocumentSaveDone },
157             { "OnSaveAs",       &DocumentEventListener::onDocumentSaveAs },
158             { "OnSaveAsDone",   &DocumentEventListener::onDocumentSaveAsDone },
159             { "OnUnload",       &DocumentEventListener::onDocumentClosed },
160             { "OnTitleChanged", &DocumentEventListener::onDocumentTitleChanged },
161             { "OnModeChanged",  &DocumentEventListener::onDocumentModeChanged }
162         };
163 
164         for ( size_t i=0; i < sizeof( aEvents ) / sizeof( aEvents[0] ); ++i )
165         {
166             if ( !_rEvent.EventName.equalsAscii( aEvents[i].pEventName ) )
167                 continue;
168 
169             ScriptDocument aDocument( xDocument );
170             {
171                 // the listener implementations usually require the SolarMutex, so lock it here.
172                 // But ensure the proper order of locking the solar and the own mutex
173                 aGuard.clear();
174                 ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
175                 ::osl::MutexGuard aGuard2( m_aMutex );
176 
177                 if ( impl_isDisposed_nothrow() )
178                     // somebody took the chance to dispose us -> bail out
179                     return;
180 
181                 (m_pListener->*aEvents[i].listenerMethod)( aDocument );
182             }
183             break;
184         }
185     }
186 
187     //--------------------------------------------------------------------
disposing(const csslang::EventObject &)188     void SAL_CALL DocumentEventNotifier_Impl::disposing( const csslang::EventObject& /*Event*/ ) throw (RuntimeException)
189     {
190         ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
191         ::osl::MutexGuard aGuard( m_aMutex );
192 
193         if ( !impl_isDisposed_nothrow() )
194             impl_dispose_nothrow();
195     }
196 
197     //--------------------------------------------------------------------
disposing()198     void SAL_CALL DocumentEventNotifier_Impl::disposing()
199     {
200         impl_listenerAction_nothrow( RemoveListener );
201         impl_dispose_nothrow();
202     }
203 
204     //--------------------------------------------------------------------
impl_dispose_nothrow()205     void DocumentEventNotifier_Impl::impl_dispose_nothrow()
206     {
207         m_pListener = NULL;
208         m_xModel.clear();
209     }
210 
211     //--------------------------------------------------------------------
impl_listenerAction_nothrow(ListenerAction _eAction)212     void DocumentEventNotifier_Impl::impl_listenerAction_nothrow( ListenerAction _eAction )
213     {
214         try
215         {
216             Reference< XEventBroadcaster > xBroadcaster;
217             if ( m_xModel.is() )
218                 xBroadcaster.set( m_xModel, UNO_QUERY_THROW );
219             else
220             {
221                 ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() );
222                 xBroadcaster.set(
223                     aContext.createComponent( "com.sun.star.frame.GlobalEventBroadcaster" ),
224                     UNO_QUERY_THROW );
225             }
226 
227             void ( SAL_CALL XEventBroadcaster::*listenerAction )( const Reference< XEventListener >& ) =
228                 ( _eAction == RegisterListener ) ? &XEventBroadcaster::addEventListener : &XEventBroadcaster::removeEventListener;
229             (xBroadcaster.get()->*listenerAction)( this );
230         }
231         catch( const Exception& )
232         {
233         	DBG_UNHANDLED_EXCEPTION();
234         }
235     }
236 
237 	//====================================================================
238 	//= DocumentEventNotifier
239 	//====================================================================
240 	//--------------------------------------------------------------------
DocumentEventNotifier(DocumentEventListener & _rListener,const Reference<XModel> & _rxDocument)241     DocumentEventNotifier::DocumentEventNotifier( DocumentEventListener& _rListener, const Reference< XModel >& _rxDocument )
242         :m_pImpl( new DocumentEventNotifier_Impl( _rListener, _rxDocument ) )
243     {
244     }
245 
246 	//--------------------------------------------------------------------
DocumentEventNotifier(DocumentEventListener & _rListener)247     DocumentEventNotifier::DocumentEventNotifier( DocumentEventListener& _rListener )
248         :m_pImpl( new DocumentEventNotifier_Impl( _rListener, Reference< XModel >() ) )
249     {
250     }
251 
252 	//--------------------------------------------------------------------
~DocumentEventNotifier()253     DocumentEventNotifier::~DocumentEventNotifier()
254     {
255     }
256 
257 	//--------------------------------------------------------------------
dispose()258     void DocumentEventNotifier::dispose()
259     {
260         m_pImpl->dispose();
261     }
262 
263 	//====================================================================
264 	//= DocumentEventListener
265 	//====================================================================
~DocumentEventListener()266     DocumentEventListener::~DocumentEventListener()
267     {
268     }
269 
270 //........................................................................
271 } // namespace basctl
272 //........................................................................
273