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_svtools.hxx"
30 
31 //_________________________________________________________________________________________________________________
32 //	my own includes
33 //_________________________________________________________________________________________________________________
34 #include "svtools/popupmenucontrollerbase.hxx"
35 
36 
37 //_________________________________________________________________________________________________________________
38 //	interface includes
39 //_________________________________________________________________________________________________________________
40 #include <com/sun/star/awt/XDevice.hpp>
41 #include <com/sun/star/beans/PropertyValue.hpp>
42 #include <com/sun/star/awt/MenuItemStyle.hpp>
43 #include <com/sun/star/frame/XDispatchProvider.hpp>
44 #include <com/sun/star/lang/DisposedException.hpp>
45 #include <com/sun/star/awt/XMenuExtended.hpp>
46 
47 //_________________________________________________________________________________________________________________
48 //	includes of other projects
49 //_________________________________________________________________________________________________________________
50 
51 #ifndef _VCL_MENU_HXX_
52 #include <vcl/menu.hxx>
53 #endif
54 #include <vcl/svapp.hxx>
55 #include <rtl/ustrbuf.hxx>
56 #include <rtl/logfile.hxx>
57 #include <vos/mutex.hxx>
58 
59 //_________________________________________________________________________________________________________________
60 //	Defines
61 //_________________________________________________________________________________________________________________
62 //
63 
64 using ::rtl::OUString;
65 
66 using namespace com::sun::star;
67 using namespace com::sun::star::uno;
68 using namespace com::sun::star::lang;
69 using namespace com::sun::star::frame;
70 using namespace com::sun::star::beans;
71 using namespace com::sun::star::util;
72 
73 namespace svt
74 {
75 
76 struct PopupMenuControllerBaseDispatchInfo
77 {
78 	Reference< XDispatch > mxDispatch;
79 	const URL maURL;
80 	const Sequence< PropertyValue > maArgs;
81 
82 	PopupMenuControllerBaseDispatchInfo( const Reference< XDispatch >& xDispatch, const URL& rURL, const Sequence< PropertyValue >& rArgs )
83 		: mxDispatch( xDispatch ), maURL( rURL ), maArgs( rArgs ) {}
84 };
85 
86 PopupMenuControllerBase::PopupMenuControllerBase( const Reference< XMultiServiceFactory >& xServiceManager ) :
87     ::comphelper::OBaseMutex(),
88     PopupMenuControllerBaseType(m_aMutex),
89     m_bInitialized( false ),
90     m_xServiceManager( xServiceManager )
91 {
92     if ( m_xServiceManager.is() )
93 		m_xURLTransformer.set( m_xServiceManager->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer"))),UNO_QUERY );
94 }
95 
96 PopupMenuControllerBase::~PopupMenuControllerBase()
97 {
98 }
99 
100 // protected function
101 void PopupMenuControllerBase::throwIfDisposed() throw ( RuntimeException )
102 {
103 	if (rBHelper.bDisposed || rBHelper.bInDispose)
104 		throw com::sun::star::lang::DisposedException();
105 }
106 
107 // protected function
108 void PopupMenuControllerBase::resetPopupMenu( com::sun::star::uno::Reference< com::sun::star::awt::XPopupMenu >& rPopupMenu )
109 {
110     VCLXPopupMenu* pPopupMenu = 0;
111     if ( rPopupMenu.is() && rPopupMenu->getItemCount() > 0 )
112     {
113          pPopupMenu = (VCLXPopupMenu *)VCLXMenu::GetImplementation( rPopupMenu );
114          if ( pPopupMenu )
115          {
116             vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
117 
118             PopupMenu* pVCLPopupMenu = (PopupMenu *)pPopupMenu->GetMenu();
119             pVCLPopupMenu->Clear();
120          }
121     }
122 }
123 
124 void SAL_CALL PopupMenuControllerBase::disposing()
125 {
126     // Reset our members and set disposed flag
127     osl::MutexGuard aLock( m_aMutex );
128     m_xFrame.clear();
129     m_xDispatch.clear();
130     m_xPopupMenu.clear();
131     m_xServiceManager.clear();
132 }
133 
134 // XServiceInfo
135 
136 sal_Bool SAL_CALL PopupMenuControllerBase::supportsService( const ::rtl::OUString& ServiceName ) throw (RuntimeException)
137 {
138 	const Sequence< rtl::OUString > aSNL( getSupportedServiceNames() );
139 	const rtl::OUString * pArray = aSNL.getConstArray();
140 
141     for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
142         if( pArray[i] == ServiceName )
143             return true;
144 
145     return false;
146 }
147 
148 // XEventListener
149 void SAL_CALL PopupMenuControllerBase::disposing( const EventObject& ) throw ( RuntimeException )
150 {
151     osl::MutexGuard aLock( m_aMutex );
152     m_xFrame.clear();
153     m_xDispatch.clear();
154     m_xPopupMenu.clear();
155 }
156 
157 // XMenuListener
158 void SAL_CALL PopupMenuControllerBase::highlight( const awt::MenuEvent& ) throw (RuntimeException)
159 {
160 }
161 
162 void PopupMenuControllerBase::impl_select(const Reference< XDispatch >& _xDispatch,const URL& aURL)
163 {
164     Sequence<PropertyValue>	     aArgs;
165 	OSL_ENSURE(_xDispatch.is(),"PopupMenuControllerBase::impl_select: No dispatch");
166 	if ( _xDispatch.is() )
167 		_xDispatch->dispatch( aURL, aArgs );
168 }
169 
170 void SAL_CALL PopupMenuControllerBase::select( const awt::MenuEvent& rEvent ) throw (RuntimeException)
171 {
172 	throwIfDisposed();
173 
174     osl::MutexGuard aLock( m_aMutex );
175 
176 	Reference< awt::XMenuExtended > xExtMenu( m_xPopupMenu, UNO_QUERY );
177 	if( xExtMenu.is() )
178 	{
179 		Sequence<PropertyValue> aArgs;
180 		dispatchCommand( xExtMenu->getCommand( rEvent.MenuId ), aArgs );
181 	}
182 }
183 
184 void PopupMenuControllerBase::dispatchCommand( const ::rtl::OUString& sCommandURL, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rArgs )
185 {
186     osl::MutexGuard aLock( m_aMutex );
187 
188 	throwIfDisposed();
189 
190     try
191     {
192 	    Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY_THROW );
193         URL aURL;
194         aURL.Complete = sCommandURL;
195         m_xURLTransformer->parseStrict( aURL );
196 
197 		Reference< XDispatch > xDispatch( xDispatchProvider->queryDispatch( aURL, OUString(), 0 ), UNO_QUERY_THROW );
198 
199         Application::PostUserEvent( STATIC_LINK(0, PopupMenuControllerBase, ExecuteHdl_Impl), new PopupMenuControllerBaseDispatchInfo( xDispatch, aURL, rArgs ) );
200 
201     }
202 	catch( Exception& )
203 	{
204 	}
205 
206 }
207 
208 IMPL_STATIC_LINK_NOINSTANCE( PopupMenuControllerBase, ExecuteHdl_Impl, PopupMenuControllerBaseDispatchInfo*, pDispatchInfo )
209 {
210 	pDispatchInfo->mxDispatch->dispatch( pDispatchInfo->maURL, pDispatchInfo->maArgs );
211     delete pDispatchInfo;
212     return 0;
213 }
214 
215 void SAL_CALL PopupMenuControllerBase::activate( const awt::MenuEvent& ) throw (RuntimeException)
216 {
217 }
218 
219 void SAL_CALL PopupMenuControllerBase::deactivate( const awt::MenuEvent& ) throw (RuntimeException)
220 {
221 }
222 
223 void SAL_CALL PopupMenuControllerBase::updatePopupMenu() throw ( ::com::sun::star::uno::RuntimeException )
224 {
225     osl::ClearableMutexGuard aLock( m_aMutex );
226 	throwIfDisposed();
227     aLock.clear();
228 
229 	updateCommand( m_aCommandURL );
230 }
231 
232 void SAL_CALL PopupMenuControllerBase::updateCommand( const rtl::OUString& rCommandURL )
233 {
234     osl::ClearableMutexGuard aLock( m_aMutex );
235     Reference< XStatusListener > xStatusListener( static_cast< OWeakObject* >( this ), UNO_QUERY );
236     Reference< XDispatch > xDispatch( m_xDispatch );
237     URL aTargetURL;
238     aTargetURL.Complete = rCommandURL;
239     m_xURLTransformer->parseStrict( aTargetURL );
240     aLock.clear();
241 
242     // Add/remove status listener to get a status update once
243     if ( xDispatch.is() )
244     {
245         xDispatch->addStatusListener( xStatusListener, aTargetURL );
246         xDispatch->removeStatusListener( xStatusListener, aTargetURL );
247     }
248 }
249 
250 
251 // XDispatchProvider
252 Reference< XDispatch > SAL_CALL
253 PopupMenuControllerBase::queryDispatch(
254     const URL& /*aURL*/,
255     const rtl::OUString& /*sTarget*/,
256     sal_Int32 /*nFlags*/ )
257 throw( RuntimeException )
258 {
259     // must be implemented by subclass
260     osl::MutexGuard aLock( m_aMutex );
261 	throwIfDisposed();
262 
263     return Reference< XDispatch >();
264 }
265 
266 Sequence< Reference< XDispatch > > SAL_CALL PopupMenuControllerBase::queryDispatches( const Sequence< DispatchDescriptor >& lDescriptor ) throw( RuntimeException )
267 {
268     // Create return list - which must have same size then the given descriptor
269     // It's not allowed to pack it!
270     osl::ClearableMutexGuard aLock( m_aMutex );
271 	throwIfDisposed();
272     aLock.clear();
273 
274     sal_Int32                                                          nCount = lDescriptor.getLength();
275     uno::Sequence< uno::Reference< frame::XDispatch > > lDispatcher( nCount );
276 
277     // Step over all descriptors and try to get any dispatcher for it.
278     for( sal_Int32 i=0; i<nCount; ++i )
279     {
280         lDispatcher[i] = queryDispatch( lDescriptor[i].FeatureURL  ,
281                                         lDescriptor[i].FrameName   ,
282                                         lDescriptor[i].SearchFlags );
283     }
284 
285     return lDispatcher;
286 }
287 
288 // XDispatch
289 void SAL_CALL
290 PopupMenuControllerBase::dispatch(
291     const URL& /*aURL*/,
292     const Sequence< PropertyValue >& /*seqProperties*/ )
293 throw( ::com::sun::star::uno::RuntimeException )
294 {
295     // must be implemented by subclass
296     osl::MutexGuard aLock( m_aMutex );
297 	throwIfDisposed();
298 }
299 
300 void SAL_CALL
301 PopupMenuControllerBase::addStatusListener(
302     const Reference< XStatusListener >& xControl,
303     const URL& aURL )
304 throw( ::com::sun::star::uno::RuntimeException )
305 {
306     osl::ResettableMutexGuard aLock( m_aMutex );
307 	throwIfDisposed();
308     aLock.clear();
309 
310     bool bStatusUpdate( false );
311     rBHelper.addListener( ::getCppuType( &xControl ), xControl );
312 
313     aLock.reset();
314     if ( aURL.Complete.indexOf( m_aBaseURL ) == 0 )
315         bStatusUpdate = true;
316     aLock.clear();
317 
318     if ( bStatusUpdate )
319     {
320         // Dummy update for popup menu controllers
321         FeatureStateEvent aEvent;
322         aEvent.FeatureURL = aURL;
323         aEvent.IsEnabled  = sal_True;
324         aEvent.Requery    = sal_False;
325         aEvent.State      = Any();
326         xControl->statusChanged( aEvent );
327     }
328 }
329 
330 void SAL_CALL PopupMenuControllerBase::removeStatusListener(
331     const Reference< XStatusListener >& xControl,
332     const URL& /*aURL*/ )
333 throw( ::com::sun::star::uno::RuntimeException )
334 {
335     rBHelper.removeListener( ::getCppuType( &xControl ), xControl );
336 }
337 
338 ::rtl::OUString PopupMenuControllerBase::determineBaseURL( const ::rtl::OUString& aURL )
339 {
340     // Just use the main part of the URL for popup menu controllers
341     sal_Int32     nQueryPart( 0 );
342     sal_Int32     nSchemePart( 0 );
343     rtl::OUString aMainURL( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.popup:" ));
344 
345     nSchemePart = aURL.indexOf( ':' );
346     if (( nSchemePart > 0 ) &&
347         ( aURL.getLength() > ( nSchemePart+1 )))
348     {
349         nQueryPart  = aURL.indexOf( '?', nSchemePart );
350         if ( nQueryPart > 0 )
351             aMainURL += aURL.copy( nSchemePart, nQueryPart-nSchemePart );
352         else if ( nQueryPart == -1 )
353             aMainURL += aURL.copy( nSchemePart+1 );
354     }
355 
356     return aMainURL;
357 }
358 
359 // XInitialization
360 void SAL_CALL PopupMenuControllerBase::initialize( const Sequence< Any >& aArguments ) throw ( Exception, RuntimeException )
361 {
362     osl::MutexGuard aLock( m_aMutex );
363 
364     sal_Bool bInitalized( m_bInitialized );
365     if ( !bInitalized )
366     {
367         PropertyValue       aPropValue;
368         rtl::OUString       aCommandURL;
369         Reference< XFrame > xFrame;
370 
371         for ( int i = 0; i < aArguments.getLength(); i++ )
372         {
373             if ( aArguments[i] >>= aPropValue )
374             {
375                 if ( aPropValue.Name.equalsAscii( "Frame" ))
376                     aPropValue.Value >>= xFrame;
377                 else if ( aPropValue.Name.equalsAscii( "CommandURL" ))
378                     aPropValue.Value >>= aCommandURL;
379             }
380         }
381 
382         if ( xFrame.is() && aCommandURL.getLength() )
383         {
384             m_xFrame        = xFrame;
385             m_aCommandURL   = aCommandURL;
386             m_aBaseURL      = determineBaseURL( aCommandURL );
387             m_bInitialized  = true;
388         }
389     }
390 }
391 // XPopupMenuController
392 void SAL_CALL PopupMenuControllerBase::setPopupMenu( const Reference< awt::XPopupMenu >& xPopupMenu ) throw ( RuntimeException )
393 {
394     osl::MutexGuard aLock( m_aMutex );
395 	throwIfDisposed();
396 
397     if ( m_xFrame.is() && !m_xPopupMenu.is() )
398     {
399         // Create popup menu on demand
400         vos::OGuard aSolarMutexGuard( Application::GetSolarMutex() );
401 
402         m_xPopupMenu = xPopupMenu;
403 	    m_xPopupMenu->addMenuListener( Reference< awt::XMenuListener >( (OWeakObject*)this, UNO_QUERY ));
404 
405         Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
406 
407         URL aTargetURL;
408         aTargetURL.Complete = m_aCommandURL;
409         m_xURLTransformer->parseStrict( aTargetURL );
410         m_xDispatch = xDispatchProvider->queryDispatch( aTargetURL, ::rtl::OUString(), 0 );
411 
412         impl_setPopupMenu();
413 
414         updatePopupMenu();
415     }
416 }
417 void PopupMenuControllerBase::impl_setPopupMenu()
418 {
419 }
420 }
421