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