1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basctl.hxx"
30 
31 #include "doceventnotifier.hxx"
32 #include "scriptdocument.hxx"
33 
34 /** === begin UNO includes === **/
35 #include <com/sun/star/document/XEventBroadcaster.hpp>
36 /** === end UNO includes === **/
37 
38 #include <vcl/svapp.hxx>
39 
40 #include <tools/diagnose_ex.h>
41 
42 #include <comphelper/componentcontext.hxx>
43 #include <comphelper/processfactory.hxx>
44 
45 #include <vos/mutex.hxx>
46 
47 #include <cppuhelper/compbase1.hxx>
48 #include <cppuhelper/basemutex.hxx>
49 
50 //........................................................................
51 namespace basctl
52 {
53 //........................................................................
54 
55 	/** === begin UNO using === **/
56     using ::com::sun::star::document::XEventBroadcaster;
57     using ::com::sun::star::document::XEventListener;
58     using ::com::sun::star::document::EventObject;
59     using ::com::sun::star::uno::RuntimeException;
60     using ::com::sun::star::uno::Reference;
61     using ::com::sun::star::uno::UNO_QUERY_THROW;
62     using ::com::sun::star::uno::Exception;
63     using ::com::sun::star::frame::XModel;
64     using ::com::sun::star::uno::UNO_QUERY;
65 	/** === end UNO using === **/
66     namespace csslang = ::com::sun::star::lang;
67 
68 	//====================================================================
69 	//= DocumentEventNotifier_Impl
70 	//====================================================================
71     typedef ::cppu::WeakComponentImplHelper1    <   XEventListener
72                                                 >   DocumentEventNotifier_Impl_Base;
73 
74     enum ListenerAction
75     {
76         RegisterListener,
77         RemoveListener
78     };
79 
80     /** impl class for DocumentEventNotifier
81     */
82     class DocumentEventNotifier_Impl    :public ::boost::noncopyable
83                                         ,public ::cppu::BaseMutex
84                                         ,public DocumentEventNotifier_Impl_Base
85     {
86     public:
87         DocumentEventNotifier_Impl( DocumentEventListener& _rListener, const Reference< XModel >& _rxDocument );
88 
89         // document::XEventListener
90         virtual void SAL_CALL notifyEvent( const EventObject& Event ) throw (RuntimeException);
91 
92         // lang::XEventListener
93         virtual void SAL_CALL disposing( const csslang::EventObject& Event ) throw (RuntimeException);
94 
95         // ComponentHelper
96         virtual void SAL_CALL disposing();
97 
98     protected:
99         ~DocumentEventNotifier_Impl();
100 
101     private:
102         /// determines whether the instance is already disposed
103         bool    impl_isDisposed_nothrow() const { return m_pListener == NULL; }
104 
105         /// disposes the instance
106         void    impl_dispose_nothrow();
107 
108         /// registers or revokes the instance as listener at the global event broadcaster
109         void    impl_listenerAction_nothrow( ListenerAction _eAction );
110 
111     private:
112         DocumentEventListener*  m_pListener;
113         Reference< XModel >     m_xModel;
114     };
115 
116 	//--------------------------------------------------------------------
117     DocumentEventNotifier_Impl::DocumentEventNotifier_Impl( DocumentEventListener& _rListener, const Reference< XModel >& _rxDocument )
118         :DocumentEventNotifier_Impl_Base( m_aMutex )
119         ,m_pListener( &_rListener )
120         ,m_xModel( _rxDocument )
121     {
122         osl_incrementInterlockedCount( &m_refCount );
123         impl_listenerAction_nothrow( RegisterListener );
124         osl_decrementInterlockedCount( &m_refCount );
125     }
126 
127 	//--------------------------------------------------------------------
128     DocumentEventNotifier_Impl::~DocumentEventNotifier_Impl()
129     {
130         if ( !impl_isDisposed_nothrow() )
131         {
132             acquire();
133             dispose();
134         }
135     }
136 
137     //--------------------------------------------------------------------
138     void SAL_CALL DocumentEventNotifier_Impl::notifyEvent( const EventObject& _rEvent ) throw (RuntimeException)
139     {
140         ::osl::ClearableMutexGuard aGuard( m_aMutex );
141 
142         OSL_PRECOND( !impl_isDisposed_nothrow(), "DocumentEventNotifier_Impl::notifyEvent: disposed, but still getting events?" );
143         if ( impl_isDisposed_nothrow() )
144             return;
145 
146         Reference< XModel > xDocument( _rEvent.Source, UNO_QUERY );
147         OSL_ENSURE( xDocument.is(), "DocumentEventNotifier_Impl::notifyEvent: illegal source document!" );
148         if ( !xDocument.is() )
149             return;
150 
151         struct EventEntry
152         {
153             const sal_Char* pEventName;
154             void (DocumentEventListener::*listenerMethod)( const ScriptDocument& _rDocument );
155         };
156         EventEntry aEvents[] = {
157             { "OnNew",          &DocumentEventListener::onDocumentCreated },
158             { "OnLoad",         &DocumentEventListener::onDocumentOpened },
159             { "OnSave",         &DocumentEventListener::onDocumentSave },
160             { "OnSaveDone",     &DocumentEventListener::onDocumentSaveDone },
161             { "OnSaveAs",       &DocumentEventListener::onDocumentSaveAs },
162             { "OnSaveAsDone",   &DocumentEventListener::onDocumentSaveAsDone },
163             { "OnUnload",       &DocumentEventListener::onDocumentClosed },
164             { "OnTitleChanged", &DocumentEventListener::onDocumentTitleChanged },
165             { "OnModeChanged",  &DocumentEventListener::onDocumentModeChanged }
166         };
167 
168         for ( size_t i=0; i < sizeof( aEvents ) / sizeof( aEvents[0] ); ++i )
169         {
170             if ( !_rEvent.EventName.equalsAscii( aEvents[i].pEventName ) )
171                 continue;
172 
173             ScriptDocument aDocument( xDocument );
174             {
175                 // the listener implementations usually require the SolarMutex, so lock it here.
176                 // But ensure the proper order of locking the solar and the own mutex
177                 aGuard.clear();
178                 ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
179                 ::osl::MutexGuard aGuard2( m_aMutex );
180 
181                 if ( impl_isDisposed_nothrow() )
182                     // somebody took the chance to dispose us -> bail out
183                     return;
184 
185                 (m_pListener->*aEvents[i].listenerMethod)( aDocument );
186             }
187             break;
188         }
189     }
190 
191     //--------------------------------------------------------------------
192     void SAL_CALL DocumentEventNotifier_Impl::disposing( const csslang::EventObject& /*Event*/ ) throw (RuntimeException)
193     {
194         ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
195         ::osl::MutexGuard aGuard( m_aMutex );
196 
197         if ( !impl_isDisposed_nothrow() )
198             impl_dispose_nothrow();
199     }
200 
201     //--------------------------------------------------------------------
202     void SAL_CALL DocumentEventNotifier_Impl::disposing()
203     {
204         impl_listenerAction_nothrow( RemoveListener );
205         impl_dispose_nothrow();
206     }
207 
208     //--------------------------------------------------------------------
209     void DocumentEventNotifier_Impl::impl_dispose_nothrow()
210     {
211         m_pListener = NULL;
212         m_xModel.clear();
213     }
214 
215     //--------------------------------------------------------------------
216     void DocumentEventNotifier_Impl::impl_listenerAction_nothrow( ListenerAction _eAction )
217     {
218         try
219         {
220             Reference< XEventBroadcaster > xBroadcaster;
221             if ( m_xModel.is() )
222                 xBroadcaster.set( m_xModel, UNO_QUERY_THROW );
223             else
224             {
225                 ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() );
226                 xBroadcaster.set(
227                     aContext.createComponent( "com.sun.star.frame.GlobalEventBroadcaster" ),
228                     UNO_QUERY_THROW );
229             }
230 
231             void ( SAL_CALL XEventBroadcaster::*listenerAction )( const Reference< XEventListener >& ) =
232                 ( _eAction == RegisterListener ) ? &XEventBroadcaster::addEventListener : &XEventBroadcaster::removeEventListener;
233             (xBroadcaster.get()->*listenerAction)( this );
234         }
235         catch( const Exception& )
236         {
237         	DBG_UNHANDLED_EXCEPTION();
238         }
239     }
240 
241 	//====================================================================
242 	//= DocumentEventNotifier
243 	//====================================================================
244 	//--------------------------------------------------------------------
245     DocumentEventNotifier::DocumentEventNotifier( DocumentEventListener& _rListener, const Reference< XModel >& _rxDocument )
246         :m_pImpl( new DocumentEventNotifier_Impl( _rListener, _rxDocument ) )
247     {
248     }
249 
250 	//--------------------------------------------------------------------
251     DocumentEventNotifier::DocumentEventNotifier( DocumentEventListener& _rListener )
252         :m_pImpl( new DocumentEventNotifier_Impl( _rListener, Reference< XModel >() ) )
253     {
254     }
255 
256 	//--------------------------------------------------------------------
257     DocumentEventNotifier::~DocumentEventNotifier()
258     {
259     }
260 
261 	//--------------------------------------------------------------------
262     void DocumentEventNotifier::dispose()
263     {
264         m_pImpl->dispose();
265     }
266 
267 	//====================================================================
268 	//= DocumentEventListener
269 	//====================================================================
270     DocumentEventListener::~DocumentEventListener()
271     {
272     }
273 
274 //........................................................................
275 } // namespace basctl
276 //........................................................................
277